Merge pull request #1104 from PySimpleGUI/Dev-latest

Dev latest
This commit is contained in:
MikeTheWatchGuy 2019-01-23 16:21:35 -05:00 committed by GitHub
commit 434bd9ffcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 146 additions and 49 deletions

View File

@ -48,7 +48,7 @@ DEFAULT_BASE64_ICON = b'iVBORw0KGgoAAAANSUhEUgAAACEAAAAgCAMAAACrZuH4AAAABGdBTUEA
# ----====----====----==== Constants the user CAN safely change ====----====----====----# # ----====----====----==== Constants the user CAN safely change ====----====----====----#
DEFAULT_WINDOW_ICON = 'default_icon.ico' DEFAULT_WINDOW_ICON = 'default_icon.ico'
DEFAULT_ELEMENT_SIZE = (45, 1) # In CHARACTERS DEFAULT_ELEMENT_SIZE = (250, 26) # In pixels
DEFAULT_BUTTON_ELEMENT_SIZE = (10, 1) # In CHARACTERS DEFAULT_BUTTON_ELEMENT_SIZE = (10, 1) # In CHARACTERS
DEFAULT_MARGINS = (10, 5) # Margins for each LEFT/RIGHT margin is first term DEFAULT_MARGINS = (10, 5) # Margins for each LEFT/RIGHT margin is first term
DEFAULT_ELEMENT_PADDING = (5, 3) # Padding between elements (row, col) in pixels DEFAULT_ELEMENT_PADDING = (5, 3) # Padding between elements (row, col) in pixels
@ -464,6 +464,7 @@ class Element():
def Update(self, widget, background_color=None, text_color=None, font=None, visible=None, disabled=None, tooltip=None): def Update(self, widget, background_color=None, text_color=None, font=None, visible=None, disabled=None, tooltip=None):
return
if font: if font:
widget.SetFont(font_to_wx_font(font)) widget.SetFont(font_to_wx_font(font))
if text_color not in (None, COLOR_SYSTEM_DEFAULT): if text_color not in (None, COLOR_SYSTEM_DEFAULT):
@ -1024,49 +1025,56 @@ class Multiline(Element):
# Text # # Text #
# ---------------------------------------------------------------------- # # ---------------------------------------------------------------------- #
class Text(Element): class Text(Element):
def __init__(self, text, size=(None, None), auto_size_text=None, click_submits=None, relief=None, font=None, def __init__(self, text, size=(None, None), auto_size_text=None, click_submits=None, enable_events=False, relief=None, border_width=None, font=None, text_color=None, background_color=None, justification=None, pad=None, margins=None, key=None, tooltip=None, visible=True, size_px=(None,None)):
text_color=None, background_color=None, justification=None, pad=None, key=None, tooltip=None): """
''' Text
Text Element
:param text: :param text:
:param size: :param size:
:param auto_size_text: :param auto_size_text:
:param click_submits: :param click_submits:
:param enable_events:
:param relief: :param relief:
:param font: :param font:
:param text_color: :param text_color:
:param background_color: :param background_color:
:param justification: :param justification:
:param pad: :param pad:
:param margins:
:param key: :param key:
:param tooltip: :param tooltip:
''' :param visible:
:param size_px:
"""
self.DisplayText = text self.DisplayText = text
self.TextColor = text_color if text_color else DEFAULT_TEXT_COLOR self.TextColor = text_color if text_color else DEFAULT_TEXT_COLOR
self.Justification = justification self.Justification = justification
self.Relief = relief self.Relief = relief
self.ClickSubmits = click_submits self.ClickSubmits = click_submits or enable_events
self.Margins = margins
self.size_px = size_px
if background_color is None: if background_color is None:
bg = DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR bg = DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR
else: else:
bg = background_color bg = background_color
self.Widget = None # type: remi.gui.Label pixelsize = size
if size[1] is not None and size[1] < 10:
pixelsize = size[0]*10, size[1]*20
self.WxStaticText:wx.StaticText = None # wx.StaticText(form.MasterPanel, -1, element.DisplayText)
self.BorderWidth = border_width if border_width is not None else DEFAULT_BORDER_WIDTH
super().__init__(ELEM_TYPE_TEXT, size, auto_size_text, background_color=bg, font=font if font else DEFAULT_FONT, super().__init__(ELEM_TYPE_TEXT, pixelsize, auto_size_text, background_color=bg, font=font if font else DEFAULT_FONT,
text_color=self.TextColor, pad=pad, key=key, tooltip=tooltip) text_color=self.TextColor, pad=pad, key=key, tooltip=tooltip, size_px=size_px, visible=visible)
return return
def Update(self, value=None, background_color=None, text_color=None, font=None): def Update(self, value=None, background_color=None, text_color=None, font=None, visible=None):
if value is not None: if value is not None:
self.DisplayText = value self.Widget.set_text(value)
stringvar = self.TKStringVar # if background_color is not None:
stringvar.set(value) # self.TKText.configure(background=background_color)
if background_color is not None: # if text_color is not None:
self.TKText.configure(background=background_color) # self.TKText.configure(fg=text_color)
if text_color is not None: # if font is not None:
self.TKText.configure(fg=text_color) # self.TKText.configure(font=font)
if font is not None:
self.TKText.configure(font=font)
def __del__(self): def __del__(self):
super().__del__() super().__del__()
@ -1442,8 +1450,7 @@ class Button(Element):
def Update(self, text=None, button_color=(None, None), disabled=None, image_data=None, image_filename=None, font=None, visible=None): def Update(self, text=None, button_color=(None, None), disabled=None, image_data=None, image_filename=None, font=None, visible=None):
if text is not None: if text is not None:
self.QT_QPushButton.setText(str(text)) self.Widget.set_text(text)
self.ButtonText = text
if self.ParentForm.Font and (self.Font == DEFAULT_FONT or not self.Font): if self.ParentForm.Font and (self.Font == DEFAULT_FONT or not self.Font):
font = self.ParentForm.Font font = self.ParentForm.Font
elif self.Font is not None: elif self.Font is not None:
@ -1465,11 +1472,11 @@ class Button(Element):
self.QT_QPushButton.setDisabled(False) self.QT_QPushButton.setDisabled(False)
# fg, bg = self.ButtonColor # fg, bg = self.ButtonColor
# print(f'Button update fg, bg {fg}, {bg}') # print(f'Button update fg, bg {fg}, {bg}')
super().Update(self.WxButton, background_color=bg, text_color=fg, font=font, visible=visible) super().Update(self.Widget, background_color=bg, text_color=fg, font=font, visible=visible)
def GetText(self): def GetText(self):
return self.ButtonText return self.Widget.get_text()
def SetFocus(self): def SetFocus(self):
self.QT_QPushButton.setFocus() self.QT_QPushButton.setFocus()
@ -2632,10 +2639,11 @@ class Window:
self.BackgroundImage = background_image self.BackgroundImage = background_image
self.XFound = False self.XFound = False
self.DisableMinimize = disable_minimize self.DisableMinimize = disable_minimize
self.App = None # type: wx.App
self.MasterFrame = None # type: wx.Frame self.MasterFrame = None # type: wx.Frame
self.MasterPanel = None # type wx.Panel self.MasterPanel = None # type wx.Panel
self.IgnoreClose = False self.IgnoreClose = False
self.thread_id = None
self.App = None # type: Window.MyApp
self.MessageQueue = Queue() self.MessageQueue = Queue()
@classmethod @classmethod
@ -2760,7 +2768,10 @@ class Window:
except: # timeout except: # timeout
self.LastButtonClicked = timeout_key self.LastButtonClicked = timeout_key
elif timeout == 0: elif timeout == 0:
self.LastButtonClicked = self.MessageQueue.get_nowait() 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('--------------------- BACK FROM MESSAGE QUEUE GET ----------------------') # print('--------------------- BACK FROM MESSAGE QUEUE GET ----------------------')
@ -2988,6 +2999,7 @@ class Window:
return None return None
def Close(self): def Close(self):
self.App.close()
if self.TKrootDestroyed: if self.TKrootDestroyed:
return return
# try: # try:
@ -2995,6 +3007,7 @@ class Window:
# except: # except:
# print('error closing window') # print('error closing window')
CloseNonBlockingForm = Close CloseNonBlockingForm = Close
CloseNonBlocking = Close CloseNonBlocking = Close
@ -3096,13 +3109,12 @@ class Window:
element.__del__() element.__del__()
def remi_thread(self, window): def remi_thread(self):
# print('In Remi Thread....', 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(window.MyApp, debug=False, address='0.0.0.0', port=0, start_browser=True, userdata=(window,)) # standalone=True) remi.start(self.MyApp, title=self.Title ,debug=False, address='0.0.0.0', port=0, start_browser=True, userdata=(self,)) # standalone=True)
window.MessageQueue.put(None) self.MessageQueue.put(None)
class MyApp(remi.App): class MyApp(remi.App):
def __init__(self,*args): def __init__(self,*args):
@ -3110,6 +3122,7 @@ class Window:
# print(args[-1]) # print(args[-1])
userdata = args[-1].userdata userdata = args[-1].userdata
self.window = userdata[0] # type: Window self.window = userdata[0] # type: Window
self.window.App = self
super(Window.MyApp, self).__init__(*args) super(Window.MyApp, self).__init__(*args)
def main(self, name='world'): def main(self, name='world'):
@ -3117,6 +3130,8 @@ class Window:
wid = remi.gui.VBox() wid = remi.gui.VBox()
wid.style['justify-content'] = 'flex-start' wid.style['justify-content'] = 'flex-start'
wid.style['align-items'] = 'baseline' wid.style['align-items'] = 'baseline'
wid.style['background-color'] = self.window.BackgroundColor
PackFormIntoFrame(self.window, wid, self.window) PackFormIntoFrame(self.window, wid, self.window)
# #
# lbl = remi.gui.Label("Close or reload the page, the console thread will stop automatically.") # lbl = remi.gui.Label("Close or reload the page, the console thread will stop automatically.")
@ -3182,7 +3197,7 @@ def convert_tkinter_size_to_Wx(size):
return qtsize return qtsize
def font_to_wx_font(font): def font_parse_string(font):
""" """
Convert from font string/tyuple into a Qt style sheet string Convert from font string/tyuple into a Qt style sheet string
:param font: "Arial 10 Bold" or ('Arial', 10, 'Bold) :param font: "Arial 10 Bold" or ('Arial', 10, 'Bold)
@ -3196,23 +3211,15 @@ def font_to_wx_font(font):
_font = font.split(' ') _font = font.split(' ')
else: else:
_font = font _font = font
name = _font[0]
family = _font[0] family = _font[0]
point_size = int(_font[1]) point_size = int(_font[1])
# style = _font[2] style = _font[2:] if len(_font) > 1 else None
underline = 'underline' in _font[2:] # underline = 'underline' in _font[2:]
bold = 'bold' in _font # bold = 'bold' in _font
wxfont = wx.Font(point_size, return family, point_size, style
wx.FONTFAMILY_DEFAULT,
wx.FONTSTYLE_NORMAL,
wx.FONTWEIGHT_BOLD if bold else wx.FONTWEIGHT_NORMAL,
underline,
faceName=family)
return wxfont
@ -3860,6 +3867,50 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
def CharWidthInPixels(): def CharWidthInPixels():
return tkinter.font.Font().measure('A') # single character width return tkinter.font.Font().measure('A') # single character width
def pad_widget(widget):
lrsizer = wx.BoxSizer(wx.HORIZONTAL)
if full_element_pad[1] == full_element_pad[3]: # if right = left
lrsizer.Add(widget, 0, wx.LEFT | wx.RIGHT, border=full_element_pad[1])
else:
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(widget, 0, wx.LEFT, border=full_element_pad[3])
lrsizer.Add(sizer, 0, wx.RIGHT, border=full_element_pad[1])
top_bottom_sizer = wx.BoxSizer(wx.HORIZONTAL)
if full_element_pad[0] == full_element_pad[2]: # if top = bottom
top_bottom_sizer.Add(lrsizer, 0, wx.TOP | wx.BOTTOM, border=full_element_pad[0])
else:
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(lrsizer, 0, wx.TOP, border=full_element_pad[0])
top_bottom_sizer.Add(sizer, 0, wx.BOTTOM, border=full_element_pad[2])
return top_bottom_sizer
#
# font, text color, background color, size, disabled, visible, tooltip
#
def do_font_and_color(widget):
font_info = font_parse_string(font) # family, point size, other
widget.style['font-family'] = font_info[0]
if element.BackgroundColor not in (None, COLOR_SYSTEM_DEFAULT):
widget.style['background-color'] = element.BackgroundColor
if element.TextColor not in (None, COLOR_SYSTEM_DEFAULT):
widget.style['color'] = element.TextColor
widget.style['font-size'] = '{}px'.format(font_info[1])
size = convert_tkinter_size_to_Wx(element_size)
widget.style['height'] = '{}px'.format(size[1])
widget.style['width'] = '{}px'.format(size[0])
#
# widget.SetMinSize(element_size)
# if element.Disabled:
# widget.Enable(False)
# if not element.Visible:
# widget.Hide()
# if element.Tooltip:
# widget.SetToolTip(element.Tooltip)
border_depth = toplevel_form.BorderDepth if toplevel_form.BorderDepth is not None else DEFAULT_BORDER_WIDTH border_depth = toplevel_form.BorderDepth if toplevel_form.BorderDepth is not None else DEFAULT_BORDER_WIDTH
# --------------------------------------------------------------------------- # # --------------------------------------------------------------------------- #
# **************** Use FlexForm to build the tkinter window ********** ----- # # **************** Use FlexForm to build the tkinter window ********** ----- #
@ -3875,6 +3926,8 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
tk_row_frame = remi.gui.HBox() # TODO Get horizontal ROW widget to put others into tk_row_frame = remi.gui.HBox() # TODO Get horizontal ROW widget to put others into
tk_row_frame.style['justify-content'] = 'flex-start' tk_row_frame.style['justify-content'] = 'flex-start'
tk_row_frame.style['align-items'] = 'baseline' tk_row_frame.style['align-items'] = 'baseline'
tk_row_frame.style['background-color'] = toplevel_form.BackgroundColor
for col_num, element in enumerate(flex_row): for col_num, element in enumerate(flex_row):
element.ParentForm = toplevel_form # save the button's parent form object element.ParentForm = toplevel_form # save the button's parent form object
if toplevel_form.Font and (element.Font == DEFAULT_FONT or not element.Font): if toplevel_form.Font and (element.Font == DEFAULT_FONT or not element.Font):
@ -3933,6 +3986,13 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element = element # type: Text element = element # type: Text
element.Widget = remi.gui.Label(element.DisplayText) element.Widget = remi.gui.Label(element.DisplayText)
element.Widget.set_layout_orientation(True) element.Widget.set_layout_orientation(True)
do_font_and_color(element.Widget)
if element.Justification:
if element.Justification.startswith('c'):
element.Widget.style['text-align'] = 'center'
elif element.Justification.startswith('r'):
element.Widget.style['text-align'] = 'right'
tk_row_frame.append(element.Widget) tk_row_frame.append(element.Widget)
# auto_size_text = element.AutoSizeText # auto_size_text = element.AutoSizeText
@ -3986,8 +4046,10 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
elif element_type == ELEM_TYPE_BUTTON: elif element_type == ELEM_TYPE_BUTTON:
element = element # type: Button element = element # type: Button
size = convert_tkinter_size_to_Wx(element_size) size = convert_tkinter_size_to_Wx(element_size)
print(size)
element.Widget = remi.gui.Button(element.ButtonText, width=size[0], height=size[1], margin='10px') element.Widget = remi.gui.Button(element.ButtonText, width=size[0], height=size[1], margin='10px')
element.Widget.onclick.connect(element.ButtonCallBack) element.Widget.onclick.connect(element.ButtonCallBack)
do_font_and_color(element.Widget)
tk_row_frame.append(element.Widget) tk_row_frame.append(element.Widget)
# stringvar = tk.StringVar() # stringvar = tk.StringVar()
@ -4069,6 +4131,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form):
element.Widget = remi.gui.TextInput() element.Widget = remi.gui.TextInput()
if element.DefaultText: if element.DefaultText:
element.Widget.set_value(element.DefaultText) element.Widget.set_value(element.DefaultText)
do_font_and_color(element.Widget)
tk_row_frame.append(element.Widget) tk_row_frame.append(element.Widget)
# default_text = element.DefaultText # default_text = element.DefaultText
@ -4795,8 +4858,9 @@ def StartupTK(window:Window):
# master.update_idletasks() # don't forget # master.update_idletasks() # don't forget
thread = threading.Thread(target=window.remi_thread, args=(window,), daemon=True) window.thread_id = threading.Thread(target=window.remi_thread, daemon=True)
thread.start() window.thread_id.daemon = True
window.thread_id.start()
item = window.MessageQueue.get() # Get the layout complete message item = window.MessageQueue.get() # Get the layout complete message
@ -6393,7 +6457,8 @@ def PopupGetText(message, default_text='', password_char='', size=(None, None),
def main(): def main():
layout = [[Text('You are running the PySimpleGUI.py file itself')], SetOptions(background_color='blue', text_element_background_color='blue', text_color='white')
layout = [[Text('You are running the PySimpleGUI.py file itself', background_color='blue', font='Courier 20')],
[Text('You should be importing it rather than running it', size=(50, 2))], [Text('You should be importing it rather than running it', size=(50, 2))],
[Text('Here is your sample input window....')], [Text('Here is your sample input window....')],
[Text('Source Folder',justification='right'), InputText('Source', focus=True), [Text('Source Folder',justification='right'), InputText('Source', focus=True),
@ -6401,7 +6466,7 @@ def main():
[Text('Destination Folder', justification='right'), InputText('Dest'), FolderBrowse()], [Text('Destination Folder', justification='right'), InputText('Dest'), FolderBrowse()],
[Ok(), Cancel()]] [Ok(), Cancel()]]
window = Window('Demo window..').Layout(layout) window = Window('Demo window..', background_color='blue', font='Courier 18').Layout(layout)
while True: while True:
event, values = window.Read() event, values = window.Read()
print(event, values) print(event, values)

View File

@ -28,13 +28,45 @@ PySimpleGUIWeb enables you to run your PySimpleGUI programs in your web browser.
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 (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.
## Engineering Pre-Release Version 0.1.0 ## Engineering Pre-Release Version 0.1.0
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142) [Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
Having trouble? Visit the [GitHub site ](http://www.PySimpleGUI.com) and log an Issue. Having trouble? Visit the [GitHub site ](http://www.PySimpleGUI.com) and log an Issue.
## Installation
Installation is quite simple:
`pip install pysimpleguiweb`
Should this not work, you can copy and paste the file PySimpleGUIWeb.py into your application folder.
## Using
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.
## Requirements
PySimpleGUIWeb is based on the Remi project. You will need to install Remi prior to running PySimpleGUIWeb:
`pip install remi`
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.
## What Works ## What Works
Text Element Text Element