diff --git a/Demo_All_Widgets.py b/Demo_All_Widgets.py new file mode 100644 index 00000000..51d70237 --- /dev/null +++ b/Demo_All_Widgets.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +import sys +if sys.version_info[0] >= 3: + import PySimpleGUI as sg +else: + import PySimpleGUI27 as sg + +sg.ChangeLookAndFeel('GreenTan') + +# ------ Menu Definition ------ # +menu_def = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']], + ['&Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ], + ['&Help', '&About...'], ] + +# ------ Column Definition ------ # +column1 = [[sg.Text('Column 1', background_color='lightblue', justification='center', size=(10, 1))], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')], + [sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]] + +layout = [ + [sg.Menu(menu_def, tearoff=True)], + [sg.Text('(Almost) All widgets in one Window!', size=(30, 1), justification='center', font=("Helvetica", 25), relief=sg.RELIEF_RIDGE)], + [sg.Text('Here is some text.... and a place to enter text')], + [sg.InputText('This is my text')], + [sg.Frame(layout=[ + [sg.Checkbox('Checkbox', size=(10,1)), sg.Checkbox('My second checkbox!', default=True)], + [sg.Radio('My first Radio! ', "RADIO1", default=True, size=(10,1)), sg.Radio('My second Radio!', "RADIO1")]], title='Options',title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='Use these to set flags')], + [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)), + sg.Multiline(default_text='A second multi-line', size=(35, 3))], + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)), + sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)], + [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)), + sg.Frame('Labelled Group',[[ + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25, tick_interval=25), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75), + sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10), + sg.Column(column1, background_color='lightblue')]])], + [sg.Text('_' * 80)], + [sg.Text('Choose A Folder', size=(35, 1))], + [sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'), + sg.InputText('Default Folder'), sg.FolderBrowse()], + [sg.Submit(tooltip='Click to submit this form'), sg.Cancel()]] + +window = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout) +event, values = window.Read() + +sg.Popup('Title', + 'The results of the window.', + 'The button clicked was "{}"'.format(event), + 'The values are', values) + + diff --git a/PySimpleGUIQt/Demo_HowDoI.py b/PySimpleGUIQt/Demo_HowDoI.py new file mode 100644 index 00000000..0eaa947a --- /dev/null +++ b/PySimpleGUIQt/Demo_HowDoI.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +import sys +import PySimpleGUIQt as sg +import subprocess + + +# Test this command in a dos window if you are having trouble. +HOW_DO_I_COMMAND = 'python -m howdoi.howdoi' + +# if you want an icon on your taskbar for this gui, then change this line of code to point to the ICO file +DEFAULT_ICON = 'question.ico' + +def HowDoI(): + ''' + Make and show a window (PySimpleGUI form) that takes user input and sends to the HowDoI web oracle + Excellent example of 2 GUI concepts + 1. Output Element that will show text in a scrolled window + 2. Non-Window-Closing Buttons - These buttons will cause the form to return with the form's values, but doesn't close the form + :return: never returns + ''' + # ------- Make a new Window ------- # + sg.ChangeLookAndFeel('GreenTan') # give our form a spiffy set of colors + + layout = [ + [sg.Text('Ask and your answer will appear here....')], + [sg.Output(size=(900, 500), font=('Courier', 10))], + [ sg.Spin(values=(1, 4), initial_value=1, size=(50, 25), key='Num Answers', font=('Helvetica', 15)), + sg.Text('Num Answers',font=('Helvetica', 15), size=(170,22)), sg.Checkbox('Display Full Text', key='full text', font=('Helvetica', 15), size=(200,22)), + sg.T('Command History', font=('Helvetica', 15)), sg.T('', size=(100,25), text_color=sg.BLUES[0], key='history'), sg.Stretch()], + [sg.Multiline(size=(600, 100), enter_submits=True, focus=True, key='query', do_not_clear=False), sg.Stretch(), + sg.ReadButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0]), bind_return_key=True), + sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0])), sg.Stretch()] + ] + + window = sg.Window('How Do I ??', + default_element_size=(100, 25), + # element_padding=(10,10), + icon=DEFAULT_ICON, + font=('Helvetica',14), + default_button_element_size=(70,50), + return_keyboard_events=True, + no_titlebar=False, + grab_anywhere=True,) + + window.Layout(layout) + # ---===--- Loop taking in user input and using it to query HowDoI --- # + command_history = [] + history_offset = 0 + while True: + event, values = window.Read() + if event == 'SEND' or event == 'query': + # window.FindElement('+OUTPUT+').Update('test of output') # manually clear input because keyboard events blocks clear + + query = values['query'].rstrip() + # print(query) + QueryHowDoI(query, values['Num Answers'], values['full text']) # send the string to HowDoI + command_history.append(query) + history_offset = len(command_history)-1 + window.FindElement('query').Update('') # manually clear input because keyboard events blocks clear + window.FindElement('history').Update('\n'.join(command_history[-3:])) + elif event == None or event == 'EXIT': # if exit button or closed using X + break + elif 'Up' in event and len(command_history): # scroll back in history + command = command_history[history_offset] + history_offset -= 1 * (history_offset > 0) # decrement is not zero + window.FindElement('query').Update(command) + elif 'Down' in event and len(command_history): # scroll forward in history + history_offset += 1 * (history_offset < len(command_history)-1) # increment up to end of list + command = command_history[history_offset] + window.FindElement('query').Update(command) + elif 'Escape' in event: # clear currently line + window.FindElement('query').Update('') + + +def QueryHowDoI(Query, num_answers, full_text): + ''' + Kicks off a subprocess to send the 'Query' to HowDoI + Prints the result, which in this program will route to a gooeyGUI window + :param Query: text english question to ask the HowDoI web engine + :return: nothing + ''' + howdoi_command = HOW_DO_I_COMMAND + full_text_option = ' -a' if full_text else '' + t = subprocess.Popen(howdoi_command + ' \"'+ Query + '\" -n ' + str(num_answers)+full_text_option, stdout=subprocess.PIPE) + (output, err) = t.communicate() + print('{:^88}'.format(Query.rstrip())) + print('_'*60) + print(output.decode("utf-8") ) + exit_code = t.wait() + +if __name__ == '__main__': + HowDoI() + diff --git a/PySimpleGUI_Qt/PySimpleGUI_Qt.py b/PySimpleGUIQt/PySimpleGUIQt.py similarity index 95% rename from PySimpleGUI_Qt/PySimpleGUI_Qt.py rename to PySimpleGUIQt/PySimpleGUIQt.py index 9ebb398c..a1836557 100644 --- a/PySimpleGUI_Qt/PySimpleGUI_Qt.py +++ b/PySimpleGUIQt/PySimpleGUIQt.py @@ -6,11 +6,12 @@ import textwrap import pickle import calendar from PySide2.QtWidgets import QApplication, QLabel, QWidget, QLineEdit, QComboBox, QFormLayout, QVBoxLayout, QHBoxLayout, QListWidget, QDial -from PySide2.QtWidgets import QSlider, QCheckBox, QRadioButton, QSpinBox, QPushButton, QTextEdit, QMainWindow, QDialog +from PySide2.QtWidgets import QSlider, QCheckBox, QRadioButton, QSpinBox, QPushButton, QTextEdit, QMainWindow, QDialog, QAbstractItemView from PySide2.QtWidgets import QSpacerItem, QFrame, QGroupBox, QTextBrowser, QPlainTextEdit, QButtonGroup, QFileDialog # from PySide2.QtWidgets import -from PySide2.QtCore import Qt,QProcess +from PySide2.QtCore import Qt,QProcess, QEvent import PySide2.QtGui as QtGui +import PySide2.QtCore as QtCore import PySide2.QtWidgets as QtWidgets @@ -63,10 +64,12 @@ def TimerStop(): # ----====----====----==== Constants the user CAN safely change ====----====----====----# DEFAULT_WINDOW_ICON = 'default_icon.ico' -DEFAULT_ELEMENT_SIZE = (250, 30) # In PIXELS -DEFAULT_BUTTON_ELEMENT_SIZE = (80, 30 ) # In PIXELS +DEFAULT_ELEMENT_SIZE = (250, 22) # In PIXELS +DEFAULT_BUTTON_ELEMENT_SIZE = (80, 22 ) # In PIXELS 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 = (4, 2) # Padding between elements (row, col) in pixels +# DEFAULT_ELEMENT_PADDING = (0, 0) # Padding between elements (row, col) in pixels + DEFAULT_AUTOSIZE_TEXT = True DEFAULT_AUTOSIZE_BUTTONS = True DEFAULT_FONT = ("Helvetica", 10) @@ -132,7 +135,7 @@ DEFAULT_SLIDER_BORDER_WIDTH = 1 DEFAULT_SLIDER_RELIEF = 'flat' DEFAULT_FRAME_RELIEF = 'groove' -DEFAULT_LISTBOX_SELECT_MODE = 'single' +DEFAULT_LISTBOX_SELECT_MODE = 'extended' SELECT_MODE_MULTIPLE = 'multiple' LISTBOX_SELECT_MODE_MULTIPLE = 'multiple' SELECT_MODE_BROWSE = 'browse' @@ -141,6 +144,8 @@ SELECT_MODE_EXTENDED = 'extended' LISTBOX_SELECT_MODE_EXTENDED = 'extended' SELECT_MODE_SINGLE = 'single' LISTBOX_SELECT_MODE_SINGLE = 'single' +SELECT_MODE_CONTIGUOUS = 'contiguous' +LISTBOX_SELECT_MODE_CONTIGUOUS = 'contiguous' TABLE_SELECT_MODE_NONE = 'NONE' TABLE_SELECT_MODE_BROWSE = 'BROWSE' @@ -436,7 +441,7 @@ class InputText(Element): fg = text_color if text_color is not None else DEFAULT_INPUT_TEXT_COLOR self.Focus = focus self.do_not_clear = do_not_clear - self.Justification = justification + self.Justification = justification or 'left' self.Disabled = disabled self.ChangeSubmits = change_submits super().__init__(ELEM_TYPE_INPUT_TEXT, size=size, background_color=bg, text_color=fg, key=key, pad=pad, @@ -475,7 +480,7 @@ Input = InputText class Combo(Element): def __init__(self, values, default_value=None, size=(None, None), auto_size_text=None, background_color=None, text_color=None, change_submits=False, disabled=False, key=None, pad=None, tooltip=None, - readonly=False, font=None): + readonly=False, visible_items=10, font=None): ''' Input Combo Box Element (also called Dropdown box) :param values: @@ -492,6 +497,7 @@ class Combo(Element): self.Readonly = readonly 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.VisibleItems = visible_items super().__init__(ELEM_TYPE_INPUT_COMBO, 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) @@ -645,6 +651,8 @@ class Listbox(Element): self.SelectMode = SELECT_MODE_MULTIPLE elif select_mode == LISTBOX_SELECT_MODE_SINGLE: self.SelectMode = SELECT_MODE_SINGLE + elif select_mode == LISTBOX_SELECT_MODE_CONTIGUOUS: + self.SelectMode = SELECT_MODE_CONTIGUOUS else: self.SelectMode = DEFAULT_LISTBOX_SELECT_MODE bg = background_color if background_color else DEFAULT_INPUT_ELEMENTS_COLOR @@ -843,7 +851,7 @@ class Spin(Element): # ---------------------------------------------------------------------- # # Multiline # # ---------------------------------------------------------------------- # -class Multiline(Element): +class Multiline(Element, QWidget): 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, @@ -879,10 +887,29 @@ class Multiline(Element): text_color=fg, key=key, pad=pad, tooltip=tooltip, font=font or DEFAULT_FONT) return + + class MultiQWidget(QWidget): + def __init__(self, qt_textedit, element): + self.QT_TextEdit = qt_textedit + self.Element = element + super().__init__() + + def eventFilter(self, widget, event): + if self.Element.EnterSubmits and event.type() == QEvent.KeyPress and widget is self.QT_TextEdit: + key = event.key() + if key in (Qt.Key_Return, Qt.Key_Enter): + self.Element.ReturnKeyHandler(0) + # self.Element.ParentForm.LastButtonClicked = self.Element.Key + # self.Element.ParentForm.FormRemainedOpen = True + # if self.Element.ParentForm.CurrentlyRunningMainloop: + # self.Element.ParentForm.QTApplication.exit() + return QWidget.eventFilter(self, widget, event) + + def Update(self, value=None, disabled=None, append=False, font=None): if value is not None: self.DefaultText = value - self.QT_TextEdit.setPlaceholderText(value) + self.QT_TextEdit.setText(value) if self.Autoscroll: pass @@ -899,6 +926,7 @@ class Multiline(Element): def SetFocus(self): pass + def __del__(self): super().__del__() @@ -1134,6 +1162,7 @@ class Button(Element): self.Disabled = disabled self.ChangeSubmits = change_submits self.QT_QPushButton = None + # self.temp_size = size if size != (NONE, NONE) else super().__init__(ELEM_TYPE_BUTTON, size=size, font=font, pad=pad, key=key, tooltip=tooltip, text_color=self.TextColor, background_color=self.BackgroundColor) return @@ -1636,7 +1665,6 @@ class Frame(Element): # ---------------------------------------------------------------------- # # Separator # -# Routes stdout, stderr to a scrolled window # # ---------------------------------------------------------------------- # class VerticalSeparator(Element): def __init__(self, pad=None): @@ -1656,6 +1684,28 @@ VSeperator = VerticalSeparator VSep = VerticalSeparator +# ---------------------------------------------------------------------- # +# Separator # +# ---------------------------------------------------------------------- # +class HorizontalSeparator(Element): + def __init__(self, pad=None): + ''' + VerticalSeperator - A separator that spans only 1 row in a vertical fashion + :param pad: + ''' + self.Orientation = 'horizontal' # for now only vertical works + + super().__init__(ELEM_TYPE_SEPARATOR, pad=pad) + + def __del__(self): + super().__del__() + + +HSeperator = HorizontalSeparator +HSep = HorizontalSeparator + + + # ---------------------------------------------------------------------- # # Tab # # ---------------------------------------------------------------------- # @@ -1864,16 +1914,7 @@ class Slider(Element): text_color=text_color, key=key, pad=pad, tooltip=tooltip) return - def Qt_init(self): - self.QT_Slider = QSlider() - if self.Orientation.startswith('h'): - self.QT_Slider.setOrientation(Qt.Horizontal) - else: - self.QT_Slider.setOrientation(Qt.Vertical) - self.QT_Slider.setMinimum(self.Range[0]) - self.QT_Slider.setMaximum(self.Range[1]) - self.QT_Slider.setTickPosition(QSlider.TicksBothSides) - self.QT_Slider.setTickInterval(self.Range[1] / 10) + def Update(self, value=None, range=(None, None), disabled=None): if value is not None: @@ -2341,7 +2382,7 @@ class ErrorElement(Element): class Window: 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), button_color=None, font=None, + auto_size_text=None, auto_size_buttons=None, location=(None, None), size=(None, None), element_padding=None, button_color=None, font=None, progress_bar_color=(None, None), background_color=None, border_depth=None, auto_close=False, auto_close_duration=DEFAULT_AUTOCLOSE_TIME, icon=DEFAULT_WINDOW_ICON, force_toplevel=False, alpha_channel=1, return_keyboard_events=False, use_default_focus=True, text_justification=None, @@ -2420,6 +2461,9 @@ class Window: self.DisableClose = disable_close self._Hidden = False self.QTApplication = None + self.Size=size + self.ElementPadding = element_padding or DEFAULT_ELEMENT_PADDING + self.FocusElement = None # ------------------------- Add ONE Row to Form ------------------------- # def AddRow(self, *args): @@ -2738,10 +2782,9 @@ class Window: if self.TKrootDestroyed: return try: - self.TKroot.destroy() - _my_windows.Decrement() + self.QTWindow.close() except: - pass + print('error closing window') CloseNonBlockingForm = Close CloseNonBlocking = Close @@ -2808,19 +2851,19 @@ class Window: def CurrentLocation(self): return int(self.TKroot.winfo_x()), int(self.TKroot.winfo_y()) - @property - def Size(self): - win_width = self.TKroot.winfo_width() - win_height = self.TKroot.winfo_height() - return win_width, win_height - - @Size.setter - def Size(self, size): - try: - self.TKroot.geometry("%sx%s" % (size[0], size[1])) - self.TKroot.update_idletasks() - except: - pass + # @property + # def Size(self): + # win_width = self.TKroot.winfo_width() + # win_height = self.TKroot.winfo_height() + # return win_width, win_height + # + # @Size.setter + # def Size(self, size): + # try: + # self.TKroot.geometry("%sx%s" % (size[0], size[1])) + # self.TKroot.update_idletasks() + # except: + # pass def __enter__(self): return self @@ -3196,7 +3239,8 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): if element.Type == ELEM_TYPE_INPUT_TEXT: value = element.QT_QLineEdit.text() if not top_level_form.NonBlocking and not element.do_not_clear and not top_level_form.ReturnKeyboardEvents: - pass + element.QT_QLineEdit.setText('') + elif element.Type == ELEM_TYPE_INPUT_CHECKBOX: value = element.QT_Checkbox.isChecked() elif element.Type == ELEM_TYPE_INPUT_RADIO: @@ -3234,6 +3278,8 @@ def BuildResultsForSubform(form, initialize_only, top_level_form): value = element.QT_Slider.value() elif element.Type == ELEM_TYPE_INPUT_MULTILINE: value = element.QT_TextEdit.toPlainText() + if not top_level_form.NonBlocking and not element.do_not_clear and not top_level_form.ReturnKeyboardEvents: + element.QT_TextEdit.setText('') elif element.Type == ELEM_TYPE_TAB_GROUP: value = 0 elif element.Type == ELEM_TYPE_TABLE: @@ -3494,6 +3540,16 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element_size = toplevel_win.DefaultButtonElementSize else: auto_size_text = False # if user has specified a size then it shouldn't autosize + full_element_pad = [0,0,0,0] # left, top, bottom, right + if type(element.Pad[0]) != tuple: + full_element_pad[0] = full_element_pad[3] = element.Pad[0] + else: + full_element_pad[0], full_element_pad[3] = element.Pad[0] + if type(element.Pad[1]) != tuple: + full_element_pad[1] = full_element_pad[2] = element.Pad[1] + else: + full_element_pad[1], full_element_pad[2] = element.Pad[1] + element_pad = full_element_pad # ------------------------- COLUMN element ------------------------- # if element_type == ELEM_TYPE_COLUMN: column_widget = QWidget() @@ -3505,9 +3561,10 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element.BackgroundColor is not None: style += 'background-color: %s;' % element.BackgroundColor column_widget.setStyleSheet(style) - print(style) + # print(style) column_layout = QFormLayout() column_vbox = QVBoxLayout() + PackFormIntoFrame(element, column_layout, toplevel_win) column_vbox.addLayout(column_layout) column_widget.setLayout(column_vbox) @@ -3537,6 +3594,9 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element.Tooltip: element.QT_Label.setToolTip(element.Tooltip) + element.QT_Label.setMargin(full_element_pad[0]) + element.QT_Label.setIndent(full_element_pad[1]) + # qt_row_layout.setContentsMargins(*full_element_pad) qt_row_layout.addWidget(element.QT_Label) # ------------------------- BUTTON element ------------------------- # elif element_type == ELEM_TYPE_BUTTON: @@ -3552,7 +3612,7 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element.BackgroundColor is not None: style += 'background-color: %s;' % element.BackgroundColor element.QT_QPushButton.setStyleSheet(style) - if element.AutoSizeButton is False or toplevel_win.AutoSizeButtons is False or element.Size[0] is not None: + if element.AutoSizeButton is False or toplevel_win.AutoSizeButtons is False or element_size[0] is not None: if element_size[0] is not None: element.QT_QPushButton.setFixedWidth(element_size[0]) if element_size[1] is not None: @@ -3562,12 +3622,20 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_QPushButton.setDisabled(True) qt_row_layout.addWidget(element.QT_QPushButton) + element.QT_QPushButton.setContentsMargins(*full_element_pad) + element.QT_QPushButton.clicked.connect(element.ButtonCallBack) # element.QT_QPushButton.clicked.connect(window.QTApplication.exit) # ------------------------- INPUT (Single Line) element ------------------------- # elif element_type == ELEM_TYPE_INPUT_TEXT: default_text = element.DefaultText element.QT_QLineEdit = QLineEdit() + + if element.Justification[0] == 'c': + element.QT_QLineEdit.setAlignment(Qt.AlignCenter) + elif element.Justification[0] == 'r': + element.QT_QLineEdit.setAlignment(Qt.AlignRight) + element.QT_QLineEdit.setPlaceholderText(default_text) style = '' if font is not None: @@ -3577,6 +3645,7 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): style += 'color: %s;' % element.TextColor if element.BackgroundColor is not None: style += 'background-color: %s;' % element.BackgroundColor + element.QT_QLineEdit.setStyleSheet(style) if element.AutoSizeText is False or toplevel_win.AutoSizeButtons is False or element.Size[0] is not None: @@ -3584,7 +3653,12 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_QLineEdit.setFixedWidth(element_size[0]) if element_size[1] is not None: element.QT_QLineEdit.setFixedHeight(element_size[1]) + # element.QT_QLineEdit.setContentsMargins(*full_element_pad) + if (element.Focus or toplevel_win.UseDefaultFocus) and not focus_set: + focus_set = True + toplevel_win.FocusElement = element.QT_QLineEdit + qt_row_layout.setContentsMargins(*full_element_pad) qt_row_layout.addWidget(element.QT_QLineEdit) # ------------------------- COMBO BOX (Drop Down) element ------------------------- # @@ -3611,8 +3685,9 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element_size[1] is not None: element.QT_ComboBox.setFixedHeight(element_size[1]) - element.QT_ComboBox.addItems(element.Values) + element.QT_ComboBox.setMaxVisibleItems(element.VisibleItems) + element.QT_ComboBox.setContentsMargins(*full_element_pad) qt_row_layout.addWidget(element.QT_ComboBox) # ------------------------- OPTION MENU (Like ComboBox but different) element ------------------------- # elif element_type == ELEM_TYPE_INPUT_OPTION_MENU: @@ -3636,7 +3711,18 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element_size[1] is not None: element.QT_ListWidget.setFixedHeight(element_size[1]) + if element.SelectMode == SELECT_MODE_MULTIPLE: + element.QT_ListWidget.setSelectionMode(QAbstractItemView.MultiSelection) + elif element.SelectMode == SELECT_MODE_EXTENDED: + element.QT_ListWidget.setSelectionMode(QAbstractItemView.ExtendedSelection) + elif element.SelectMode == SELECT_MODE_CONTIGUOUS: + element.QT_ListWidget.setSelectionMode(QAbstractItemView.ContiguousSelection) + elif element.SelectMode == SELECT_MODE_SINGLE: + element.QT_ListWidget.setSelectionMode(QAbstractItemView.SingleSelection) + element.QT_ListWidget.addItems(element.Values) + qt_row_layout.setContentsMargins(*full_element_pad) + qt_row_layout.addWidget(element.QT_ListWidget) # ------------------------- INPUT MULTI LINE element ------------------------- # elif element_type == ELEM_TYPE_INPUT_MULTILINE: @@ -3660,6 +3746,15 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_TextEdit.setFixedHeight(element_size[1]) element.QT_TextEdit.setPlaceholderText(default_text) + qt_row_layout.setContentsMargins(*full_element_pad) + + element.MultiQWidget = Multiline.MultiQWidget(element.QT_TextEdit, element) + element.QT_TextEdit.installEventFilter(element.MultiQWidget) + + if (element.Focus or toplevel_win.UseDefaultFocus) and not focus_set: + focus_set = True + toplevel_win.FocusElement = element.QT_TextEdit + qt_row_layout.addWidget(element.QT_TextEdit) # ------------------------- OUTPUT MULTI LINE element ------------------------- # @@ -3686,12 +3781,16 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_TextBrowser.insertPlainText(default_text) element.QT_TextBrowser.moveCursor(QtGui.QTextCursor.End) + qt_row_layout.setContentsMargins(*full_element_pad) qt_row_layout.addWidget(element.QT_TextBrowser) - # ------------------------- INPUT CHECKBOX element ------------------------- # + # ------------------------- INPUT CHECKBOX element ------------------------- # elif element_type == ELEM_TYPE_INPUT_CHECKBOX: width = 0 if auto_size_text else element_size[0] default_value = element.InitialState element.QT_Checkbox = QCheckBox(element.Text) + element.QT_Checkbox.setChecked(element.InitialState) + if element.Disabled: + element.QT_Checkbox.setDisabled(True) style = '' if font is not None: style += 'font-family: %s;'%font[0] @@ -3707,13 +3806,13 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_Checkbox.setFixedWidth(element_size[0]) if element_size[1] is not None: element.QT_Checkbox.setFixedHeight(element_size[1]) - + qt_row_layout.setContentsMargins(*full_element_pad) qt_row_layout.addWidget(element.QT_Checkbox) # ------------------------- PROGRESS BAR element ------------------------- # elif element_type == ELEM_TYPE_PROGRESS_BAR: # save this form because it must be 'updated' (refreshed) solely for the purpose of updating bar width = element_size[0] - # ------------------------- INPUT RADIO BUTTON element ------------------------- # + # ------------------------- INPUT RADIO BUTTON element ------------------------- # elif element_type == ELEM_TYPE_INPUT_RADIO: width = 0 if auto_size_text else element_size[0] default_value = element.InitialState @@ -3734,7 +3833,17 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_Radio_Button.setFixedWidth(element_size[0]) if element_size[1] is not None: element.QT_Radio_Button.setFixedHeight(element_size[1]) + qt_row_layout.setContentsMargins(*full_element_pad) + if element.GroupID in toplevel_win.RadioDict: + QT_RadioButtonGroup = toplevel_win.RadioDict[element.GroupID] + else: + QT_RadioButtonGroup = QButtonGroup(toplevel_win.QTApplication) + toplevel_win.RadioDict[element.GroupID] = QT_RadioButtonGroup + + QT_RadioButtonGroup.addButton(element.QT_Radio_Button) + + qt_row_layout.setContentsMargins(*full_element_pad) qt_row_layout.addWidget(element.QT_Radio_Button) # ------------------------- INPUT SPIN Box element ------------------------- # @@ -3758,8 +3867,10 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element_size[1] is not None: element.QT_Spinner.setFixedHeight(element_size[1]) + qt_row_layout.setContentsMargins(*full_element_pad) + qt_row_layout.addWidget(element.QT_Spinner) - # ------------------------- OUTPUT element ------------------------- # + # ------------------------- OUTPUT element ------------------------- # elif element_type == ELEM_TYPE_OUTPUT: element.QT_TextBrowser = QTextBrowser() element.QT_TextBrowser.setDisabled(False) @@ -3781,14 +3892,16 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_TextBrowser.moveCursor(QtGui.QTextCursor.End) element.reroute_stdout() + qt_row_layout.setContentsMargins(*full_element_pad) + qt_row_layout.addWidget(element.QT_TextBrowser) - # ------------------------- IMAGE element ------------------------- # + # ------------------------- IMAGE element ------------------------- # elif element_type == ELEM_TYPE_IMAGE: pass - # ------------------------- Canvas element ------------------------- # + # ------------------------- Canvas element ------------------------- # elif element_type == ELEM_TYPE_CANVAS: width, height = element_size - # ------------------------- Graph element ------------------------- # + # ------------------------- Graph element ------------------------- # elif element_type == ELEM_TYPE_GRAPH: width, height = element_size # ------------------------- MENUBAR element ------------------------- # @@ -3813,25 +3926,35 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): PackFormIntoFrame(element, column_layout, toplevel_win) column_vbox.addLayout(column_layout) column_widget.setLayout(column_vbox) + qt_row_layout.setContentsMargins(*full_element_pad) + qt_row_layout.addWidget(column_widget) # ------------------------- Tab element ------------------------- # elif element_type == ELEM_TYPE_TAB: pass - # ------------------------- TabGroup element ------------------------- # + # ------------------------- TabGroup element ------------------------- # elif element_type == ELEM_TYPE_TAB_GROUP: pass - # ------------------------- SLIDER Box element ------------------------- # + # ------------------------- SLIDER Box element ------------------------- # elif element_type == ELEM_TYPE_INPUT_SLIDER: - - element.Qt_init() + element.QT_Slider = QSlider() + if element.Orientation.startswith('h'): + element.QT_Slider.setOrientation(Qt.Horizontal) + else: + element.QT_Slider.setOrientation(Qt.Vertical) + element.QT_Slider.setMinimum(element.Range[0]) + element.QT_Slider.setMaximum(element.Range[1]) + element.QT_Slider.setTickPosition(QSlider.TicksBothSides) + element.QT_Slider.setTickInterval(element.Range[1] / 10) if element_size[0] is not None: element.QT_Slider.setFixedWidth(element_size[0]) if element_size[1] is not None: element.QT_Slider.setFixedHeight(element_size[1]) - + element.QT_Slider.setValue(element.DefaultValue) + qt_row_layout.setContentsMargins(*full_element_pad) qt_row_layout.addWidget(element.QT_Slider) - # ------------------------- DIAL element ------------------------- # + # ------------------------- DIAL element ------------------------- # elif element_type == ELEM_TYPE_INPUT_DIAL: element.Qt_init() style = '' @@ -3843,8 +3966,10 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element.BackgroundColor is not None: style += 'background-color: %s;' % element.BackgroundColor element.QT_Dial.setStyleSheet(style) + qt_row_layout.setContentsMargins(*full_element_pad) + qt_row_layout.addWidget(element.QT_Dial) - # ------------------------- Stretch element ------------------------- # + # ------------------------- Stretch element ------------------------- # elif element_type == ELEM_TYPE_STRETCH: qt_row_layout.addStretch(1) # ------------------------- TABLE element ------------------------- # @@ -3852,12 +3977,15 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): pass # ------------------------- Tree element ------------------------- # elif element_type == ELEM_TYPE_TREE: + element.QT_QGraphicsLineItem = QtWidgets.QGraphicsLineItem pass # ------------------------- Separator element ------------------------- # elif element_type == ELEM_TYPE_SEPARATOR: pass # ............................DONE WITH ROW pack the row of widgets ..........................# + containing_frame.setSpacing(toplevel_win.ElementPadding[1]) + qt_row_layout.setSpacing(toplevel_win.ElementPadding[0]) containing_frame.addRow('', qt_row_layout) # done with row, pack the row of widgets @@ -3911,12 +4039,40 @@ def StartupTK(window): window.QTApplication = _my_windows.QTApplication window.QTWindow = QWidget() + + flags = 0 + if window.NoTitleBar: + flags |= Qt.FramelessWindowHint + flags |= QtCore.Qt.Tool + if window.KeepOnTop: + flags |= Qt.WindowStaysOnTopHint + if flags is not None: + window.QTWindow.setWindowFlags(flags) + if window.AlphaChannel: + window.QTWindow.setWindowOpacity(window.AlphaChannel) + if window.Size != (None, None): + window.QTWindow.resize(window.Size[0], window.Size[1]) + if window.WindowIcon is not None: + window.QTWindow.setWindowIcon(QtGui.QIcon(window.WindowIcon)) + + # window.QTWindow.setAttribute(Qt.WA_TranslucentBackground) + # shadow = QtWidgets.QGraphicsDropShadowEffect() + # shadow.setBlurRadius(9.0) + # shadow.setBlurRadius(50) + # window.QTWindow.setGraphicsEffect(shadow) + + # if window.KeepOnTop: + # window.QTWindow.setWindowFlags(Qt.WindowStaysOnTopHint) + + style = '' if window.BackgroundColor is not None: style += 'background-color: %s;' % window.BackgroundColor window.QTWindow.setStyleSheet(style) window.QTWindow.setWindowTitle(window.Title) + + if window.BackgroundColor is not None and window.BackgroundColor != COLOR_SYSTEM_DEFAULT: root = 000000 _my_windows.Increment() @@ -3929,12 +4085,18 @@ def StartupTK(window): if window.KeepOnTop: pass + + + window.QFormLayout = QFormLayout() window.QT_Box_Layout = QVBoxLayout() ConvertFlexToTK(window) window.QT_Box_Layout.addLayout(window.QFormLayout) + # shadow = QtWidgets.QGraphicsDropShadowEffect( window.QFormLayout) + # window.QTWindow.setGraphicsEffect(shadow) + # Make window visible again @@ -3952,8 +4114,16 @@ def StartupTK(window): window.CurrentlyRunningMainloop = True pass #### RUN MAIN LOOP HERE ##### window.QTWindow.setLayout(window.QT_Box_Layout) - window.QTWindow.show() + + if window.FocusElement is not None: + window.FocusElement.setFocus() + + window.QTWindow.show() ####### The thing that causes the window to be visible ###### + + window.QTApplication.exec_() + + window.CurrentlyRunningMainloop = False window.TimerCancelled = True # window.LastButtonClicked = 'Test' @@ -5495,12 +5665,11 @@ def main(): layout = [[Text('You are running the PySimpleGUI.py file itself')], [Text('You should be importing it rather than running it')], [Text('Here is your sample input window....')], - [Text('Source File', size=(150, 20), justification='right'), InputText('Source', focus=True),Stretch(), - FileBrowse(target=(ThisRow,-2))], - [Text('Destination Folder', size=(150, 20), justification='right'), InputText('Dest'), FolderBrowse()], + [Text('Source File', size=(150, 25), justification='right'), InputText('Source', focus=True), FileBrowse()], + [Text('Destination Folder', size=(150, 25), justification='right'), InputText('Dest'), FolderBrowse()], [Ok(), Cancel()]] - window = Window('Demo window..',auto_size_buttons=False, default_button_element_size=(80,30)).Layout(layout) + window = Window('Demo window..',auto_size_buttons=False, default_element_size=(280,22), default_button_element_size=(80,20)).Layout(layout) event, values = window.Read() window.Close() diff --git a/PySimpleGUIQt/Qt_All_Widgets.py b/PySimpleGUIQt/Qt_All_Widgets.py new file mode 100644 index 00000000..a4299b4a --- /dev/null +++ b/PySimpleGUIQt/Qt_All_Widgets.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +import sys +if sys.version_info[0] >= 3: + import PySimpleGUIQt as sg +else: + import PySimpleGUI27 as sg + +sg.ChangeLookAndFeel('Topanga') +# sg.SetOptions(element_padding=(0,0)) +# ------ Menu Definition ------ # +menu_def = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']], + ['&Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ], + ['&Help', '&About...'], ] + +# ------ Column Definition ------ # +column1 = [[sg.Text('Column 1', background_color='lightblue',text_color='black', justification='center', size=(100,22))], + [sg.Spin((1,10), size=(100,22))], + [sg.Spin((1,10), size=(100,22))], + [sg.Spin((1,10), size=(100,22))],] + +layout = [ + [sg.Menu(menu_def, tearoff=True)], + [sg.Text('(Almost) All widgets in one Window!', justification='c', font=("Helvetica", 25), relief=sg.RELIEF_RIDGE)], + [sg.Text('Here is some text.... and a place to enter text')], + [sg.InputText('This is my text', size=(400,22))], + [sg.Frame(layout=[ + [sg.Checkbox('Checkbox', size=(185,22)), sg.Checkbox('My second checkbox!', default=True)], + [sg.Radio('My first Radio!', "RADIO1", default=True, size=(180,22), ),sg.Radio('My second Radio!', "RADIO1")], + [sg.Radio('Third Radio!', "RADIO2", default=True, size=(180,22), ),sg.Radio('Fourth Radio!', "RADIO2")] + + ], title='Options',title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='Use these to set flags', ), sg.Stretch()], + [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(220, 80)), + sg.Multiline(default_text='A second multi-line', size=(220, 80))], + [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(150, 22)), sg.Stretch(), + sg.Slider(range=(1, 100), orientation='h', size=(300, 22), default_value=85)], + [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))], + [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(200,100), select_mode=sg.LISTBOX_SELECT_MODE_SINGLE), sg.Stretch(), + sg.Frame('Labelled Group',[[ + sg.Slider(range=(1, 100), orientation='v', default_value=25, tick_interval=25), + sg.Slider(range=(1, 100), orientation='v', default_value=75), + sg.Slider(range=(1, 100), orientation='v', default_value=10), + sg.Column(column1, background_color='red')]]), sg.Stretch()], + [sg.Text('_' * 50, justification='c')], + [sg.Text('Choose A Folder')], + [sg.Text('Your Folder'), + sg.InputText('Default Folder', size=(300,22)), sg.FolderBrowse(), sg.Stretch()], + [sg.Submit(tooltip='Click to submit this form',), sg.Cancel()]] + +window = sg.Window('Everything bagel', + grab_anywhere=False, + font=('Helvetica', 12), + no_titlebar=False, + alpha_channel=1, + keep_on_top=False, + element_padding=(2,3), + default_element_size=(100, 23), + default_button_element_size=(120,30) + ).Layout(layout) +event, values = window.Read() + +window.Close() + +sg.Popup('Title', + 'The results of the window.', + 'The button clicked was "{}"'.format(event), + 'The values are', values) + + diff --git a/PySimpleGUI_Qt/Qt_Dial.py b/PySimpleGUIQt/Qt_Dial.py similarity index 93% rename from PySimpleGUI_Qt/Qt_Dial.py rename to PySimpleGUIQt/Qt_Dial.py index 4ac86e4b..8a9cb63c 100644 --- a/PySimpleGUI_Qt/Qt_Dial.py +++ b/PySimpleGUIQt/Qt_Dial.py @@ -1,4 +1,4 @@ -import PySimpleGUI_Qt as sg +import PySimpleGUIQt as sg layout = [ [sg.Text('This is the new Dial Element!')], diff --git a/PySimpleGUI_Qt/Qt_Test.py b/PySimpleGUIQt/Qt_Test.py similarity index 85% rename from PySimpleGUI_Qt/Qt_Test.py rename to PySimpleGUIQt/Qt_Test.py index 2476846b..c0034fa7 100644 --- a/PySimpleGUI_Qt/Qt_Test.py +++ b/PySimpleGUIQt/Qt_Test.py @@ -1,9 +1,9 @@ -import PySimpleGUI_Qt as sg +import PySimpleGUIQt as sg # sg.Popup('test 1') # sg.Popup('test 2') sg.ChangeLookAndFeel('GreenTan') layout = [ - [sg.Text('Hello From PySimpleGUI_Qt!', text_color='red', tooltip='This is my tooltip', justification='c', font=('Courier', 22), key='_TEXT_')], + [sg.Text('Hello From PySimpleGUIQt!', text_color='red', tooltip='This is my tooltip', justification='c', font=('Courier', 22), key='_TEXT_')], [sg.Text('Input something here'),sg.Stretch(), sg.Input('This is an InputText Element', key='_INPUT_', font=('Any', 14))], [sg.Text('This is the new Dial Element'), sg.Dial(background_color='red'), sg.Stretch()], [sg.Combo(['Combo 1', 'Combo 2', 'Combo 3'], key='+COMBO+', size=(150,30), text_color='green')], @@ -13,7 +13,7 @@ layout = [ [sg.Checkbox('Checkbox 3'), sg.Checkbox('Checkbox 4')], [sg.Radio('Radio1', group_id=1),sg.Radio('Radio2', group_id=1)], [sg.Spin((5,8), size=(100,30))], - [sg.Multiline('This is a Multiline Element', key='+MULTI+')], + [sg.Multiline('This is a Multiline Element', size=(300,300), key='+MULTI+')], [sg.Button('My Button', size=(120,30)), sg.Exit(), sg.Button('Change', key='_CHANGE_')], ] diff --git a/PySimpleGUIQt/__pycache__/PySimpleGUIQt.cpython-36.pyc b/PySimpleGUIQt/__pycache__/PySimpleGUIQt.cpython-36.pyc new file mode 100644 index 00000000..61ba1e1c Binary files /dev/null and b/PySimpleGUIQt/__pycache__/PySimpleGUIQt.cpython-36.pyc differ diff --git a/PySimpleGUIQt/question.ico b/PySimpleGUIQt/question.ico new file mode 100644 index 00000000..c5bccb55 Binary files /dev/null and b/PySimpleGUIQt/question.ico differ diff --git a/PySimpleGUI_Qt/Qt_All_Widgets.py b/PySimpleGUI_Qt/Qt_All_Widgets.py deleted file mode 100644 index b6f913e5..00000000 --- a/PySimpleGUI_Qt/Qt_All_Widgets.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -import sys -if sys.version_info[0] >= 3: - import PySimpleGUI_Qt as sg -else: - import PySimpleGUI27 as sg - -sg.ChangeLookAndFeel('GreenTan') - -# # Column layout -# col = [[sg.Text('col Row 1', text_color='white', background_color='blue')], -# [sg.Text('col Row 2', text_color='white', background_color='blue'), sg.Input('col input 1')], -# [sg.Text('col Row 3', text_color='white', background_color='blue'), sg.Input('col input 2')]] -# # Window layout -# layout = [ [sg.In()], -# [sg.Text('test'), sg.Column(col, background_color='blue')],] -# -# # Display the window and get values -# event, values = sg.Window('Compact 1-line form with column').Layout(layout).Read() -# -# exit() -# ------ Menu Definition ------ # -menu_def = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']], - ['&Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ], - ['&Help', '&About...'], ] - -# ------ Column Definition ------ # -column1 = [[sg.Text('Column 1', background_color='lightblue',text_color='black', justification='center', size=(100,30))], - [sg.Spin((1,10), size=(100,30))], - [sg.Spin((1,10), size=(100,30))], - [sg.Spin((1,10), size=(100,30))],] - -layout = [ - [sg.Menu(menu_def, tearoff=True)], - [sg.Text('(Almost) All widgets in one Window!', size=(600, 50), justification='l', font=("Helvetica", 25), relief=sg.RELIEF_RIDGE)], - [sg.Text('Here is some text.... and a place to enter text')], - [sg.InputText('This is my text', size=(300,30))], - [sg.Frame(layout=[ - [sg.Checkbox('Checkbox', size=(185,30)), sg.Checkbox('My second checkbox!', default=True)], - [sg.Radio('My first Radio!', "RADIO1", default=True, size=(180,30), ),sg.Radio('My second Radio!', "RADIO1")]], title='Options',title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='Use these to set flags', ), sg.Stretch()], - [sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(300, 80)), - sg.Multiline(default_text='A second multi-line', size=(300, 80))], - [sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(150, 30)), - sg.Slider(range=(1, 100), orientation='h', size=(150, 30), default_value=85)], - [sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))], - [sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(200,100)), - sg.Frame('Labelled Group',[[ - sg.Slider(range=(1, 100), orientation='v', default_value=25, tick_interval=25), - sg.Slider(range=(1, 100), orientation='v', default_value=75), - sg.Slider(range=(1, 100), orientation='v', default_value=10), - sg.Column(column1, background_color='lightblue')]]), sg.Stretch()], - [sg.Text('_' * 80)], - [sg.Text('Choose A Folder')], - [sg.Text('Your Folder', auto_size_text=True, justification='right'), - sg.InputText('Default Folder', size=(300,30)), sg.FolderBrowse()], - [sg.Submit(tooltip='Click to submit this form', size=(120,30)), sg.Cancel(size=(120,30))]] - -window = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False, font=('Helvetica', 12)).Layout(layout) -event, values = window.Read() - -sg.Popup('Title', - 'The results of the window.', - 'The button clicked was "{}"'.format(event), - 'The values are', values) - -