From 588f332983755e11e74aba8910208c7ecd3e2cba Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Tue, 22 Jan 2019 11:52:45 -0500 Subject: [PATCH 1/2] Release 0.1.1 - explaining installation --- PySimpleGUIWeb/readme.md | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/PySimpleGUIWeb/readme.md b/PySimpleGUIWeb/readme.md index 2c83f469..5c436726 100644 --- a/PySimpleGUIWeb/readme.md +++ b/PySimpleGUIWeb/readme.md @@ -28,12 +28,44 @@ 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. - ## 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) 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 From 799a6bd05de976fb2d07c5527db03a151df8f403 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Wed, 23 Jan 2019 16:21:00 -0500 Subject: [PATCH 2/2] Button color, Button font, Text Font, background color, text color, size, justification, tab title, --- PySimpleGUIWeb/PySimpleGUIWeb.py | 159 ++++++++++++++++++++++--------- 1 file changed, 112 insertions(+), 47 deletions(-) diff --git a/PySimpleGUIWeb/PySimpleGUIWeb.py b/PySimpleGUIWeb/PySimpleGUIWeb.py index f6a2f5ee..e051918c 100644 --- a/PySimpleGUIWeb/PySimpleGUIWeb.py +++ b/PySimpleGUIWeb/PySimpleGUIWeb.py @@ -48,7 +48,7 @@ DEFAULT_BASE64_ICON = b'iVBORw0KGgoAAAANSUhEUgAAACEAAAAgCAMAAACrZuH4AAAABGdBTUEA # ----====----====----==== Constants the user CAN safely change ====----====----====----# 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_MARGINS = (10, 5) # Margins for each LEFT/RIGHT margin is first term 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): + return if font: widget.SetFont(font_to_wx_font(font)) if text_color not in (None, COLOR_SYSTEM_DEFAULT): @@ -1024,49 +1025,56 @@ class Multiline(Element): # Text # # ---------------------------------------------------------------------- # class Text(Element): - def __init__(self, text, size=(None, None), auto_size_text=None, click_submits=None, relief=None, font=None, - text_color=None, background_color=None, justification=None, pad=None, key=None, tooltip=None): - ''' - Text Element + 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 :param text: :param size: :param auto_size_text: :param click_submits: + :param enable_events: :param relief: :param font: :param text_color: :param background_color: :param justification: :param pad: + :param margins: :param key: :param tooltip: - ''' + :param visible: + :param size_px: + """ self.DisplayText = text self.TextColor = text_color if text_color else DEFAULT_TEXT_COLOR self.Justification = justification 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: bg = DEFAULT_TEXT_ELEMENT_BACKGROUND_COLOR else: 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, - text_color=self.TextColor, pad=pad, key=key, tooltip=tooltip) + 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, size_px=size_px, visible=visible) 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: - self.DisplayText = value - stringvar = self.TKStringVar - stringvar.set(value) - 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) + self.Widget.set_text(value) + # 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 __del__(self): 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): if text is not None: - self.QT_QPushButton.setText(str(text)) - self.ButtonText = text + self.Widget.set_text(text) if self.ParentForm.Font and (self.Font == DEFAULT_FONT or not self.Font): font = self.ParentForm.Font elif self.Font is not None: @@ -1465,11 +1472,11 @@ class Button(Element): self.QT_QPushButton.setDisabled(False) # fg, bg = self.ButtonColor # 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): - return self.ButtonText + return self.Widget.get_text() def SetFocus(self): self.QT_QPushButton.setFocus() @@ -2632,10 +2639,11 @@ class Window: self.BackgroundImage = background_image self.XFound = False self.DisableMinimize = disable_minimize - self.App = None # type: wx.App self.MasterFrame = None # type: wx.Frame self.MasterPanel = None # type wx.Panel self.IgnoreClose = False + self.thread_id = None + self.App = None # type: Window.MyApp self.MessageQueue = Queue() @classmethod @@ -2760,7 +2768,10 @@ class Window: except: # timeout self.LastButtonClicked = timeout_key elif timeout == 0: - self.LastButtonClicked = self.MessageQueue.get_nowait() + try: + self.LastButtonClicked = self.MessageQueue.get_nowait() + except: + self.LastButtonClicked = timeout_key else: self.LastButtonClicked = self.MessageQueue.get() # print('--------------------- BACK FROM MESSAGE QUEUE GET ----------------------') @@ -2988,6 +2999,7 @@ class Window: return None def Close(self): + self.App.close() if self.TKrootDestroyed: return # try: @@ -2995,6 +3007,7 @@ class Window: # except: # print('error closing window') + CloseNonBlockingForm = Close CloseNonBlocking = Close @@ -3096,13 +3109,12 @@ class Window: element.__del__() - def remi_thread(self, window): - # print('In Remi Thread....', window) + def remi_thread(self): logging.getLogger('remi').setLevel(level=logging.CRITICAL) logging.getLogger('remi').disabled = True logging.disable(logging.CRITICAL) - remi.start(window.MyApp, debug=False, address='0.0.0.0', port=0, start_browser=True, userdata=(window,)) # standalone=True) - window.MessageQueue.put(None) + 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) class MyApp(remi.App): def __init__(self,*args): @@ -3110,6 +3122,7 @@ class Window: # print(args[-1]) userdata = args[-1].userdata self.window = userdata[0] # type: Window + self.window.App = self super(Window.MyApp, self).__init__(*args) def main(self, name='world'): @@ -3117,6 +3130,8 @@ class Window: wid = remi.gui.VBox() wid.style['justify-content'] = 'flex-start' wid.style['align-items'] = 'baseline' + 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.") @@ -3182,7 +3197,7 @@ def convert_tkinter_size_to_Wx(size): return qtsize -def font_to_wx_font(font): +def font_parse_string(font): """ Convert from font string/tyuple into a Qt style sheet string :param font: "Arial 10 Bold" or ('Arial', 10, 'Bold) @@ -3196,23 +3211,15 @@ def font_to_wx_font(font): _font = font.split(' ') else: _font = font - name = _font[0] family = _font[0] point_size = int(_font[1]) - # style = _font[2] + style = _font[2:] if len(_font) > 1 else None - underline = 'underline' in _font[2:] - bold = 'bold' in _font + # underline = 'underline' in _font[2:] + # bold = 'bold' in _font - wxfont = wx.Font(point_size, - wx.FONTFAMILY_DEFAULT, - wx.FONTSTYLE_NORMAL, - wx.FONTWEIGHT_BOLD if bold else wx.FONTWEIGHT_NORMAL, - underline, - faceName=family) - - return wxfont + return family, point_size, style @@ -3860,6 +3867,50 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): def CharWidthInPixels(): 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 # --------------------------------------------------------------------------- # # **************** 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.style['justify-content'] = 'flex-start' tk_row_frame.style['align-items'] = 'baseline' + tk_row_frame.style['background-color'] = toplevel_form.BackgroundColor + for col_num, element in enumerate(flex_row): element.ParentForm = toplevel_form # save the button's parent form object 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.Widget = remi.gui.Label(element.DisplayText) 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) # auto_size_text = element.AutoSizeText @@ -3986,8 +4046,10 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): elif element_type == ELEM_TYPE_BUTTON: element = element # type: Button 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.onclick.connect(element.ButtonCallBack) + do_font_and_color(element.Widget) tk_row_frame.append(element.Widget) # stringvar = tk.StringVar() @@ -4069,6 +4131,7 @@ def PackFormIntoFrame(form, containing_frame, toplevel_form): element.Widget = remi.gui.TextInput() if element.DefaultText: element.Widget.set_value(element.DefaultText) + do_font_and_color(element.Widget) tk_row_frame.append(element.Widget) # default_text = element.DefaultText @@ -4795,8 +4858,9 @@ def StartupTK(window:Window): # master.update_idletasks() # don't forget - thread = threading.Thread(target=window.remi_thread, args=(window,), daemon=True) - thread.start() + window.thread_id = threading.Thread(target=window.remi_thread, daemon=True) + window.thread_id.daemon = True + window.thread_id.start() 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(): - 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('Here is your sample input window....')], [Text('Source Folder',justification='right'), InputText('Source', focus=True), @@ -6401,7 +6466,7 @@ def main(): [Text('Destination Folder', justification='right'), InputText('Dest'), FolderBrowse()], [Ok(), Cancel()]] - window = Window('Demo window..').Layout(layout) + window = Window('Demo window..', background_color='blue', font='Courier 18').Layout(layout) while True: event, values = window.Read() print(event, values)