From eff3b977823324f86a15fb89af4b6ac9b8ffd8ea Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Thu, 8 Nov 2018 00:55:40 -0500 Subject: [PATCH] Listbox select modes, return submits for multiline & input, max visible items in combobox, radio button groups, set icon, default focus set, --- Demo_All_Widgets.py | 54 +++++++++++++ PySimpleGUI_Qt/Demo_HowDoI.py | 93 +++++++++++++++++++++ PySimpleGUI_Qt/PySimpleGUI_Qt.py | 133 +++++++++++++++++++++---------- PySimpleGUI_Qt/question.ico | Bin 0 -> 43646 bytes 4 files changed, 237 insertions(+), 43 deletions(-) create mode 100644 Demo_All_Widgets.py create mode 100644 PySimpleGUI_Qt/Demo_HowDoI.py create mode 100644 PySimpleGUI_Qt/question.ico 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/PySimpleGUI_Qt/Demo_HowDoI.py b/PySimpleGUI_Qt/Demo_HowDoI.py new file mode 100644 index 00000000..56f152d7 --- /dev/null +++ b/PySimpleGUI_Qt/Demo_HowDoI.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +import sys +import PySimpleGUI_Qt 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/PySimpleGUI_Qt/PySimpleGUI_Qt.py index 3421d8fe..f6ad3184 100644 --- a/PySimpleGUI_Qt/PySimpleGUI_Qt.py +++ b/PySimpleGUI_Qt/PySimpleGUI_Qt.py @@ -6,10 +6,10 @@ 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.QtWidgets as QtWidgets @@ -134,7 +134,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' @@ -143,6 +143,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' @@ -477,7 +479,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: @@ -494,6 +496,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) @@ -647,6 +650,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 @@ -881,10 +886,29 @@ class Multiline(Element, QWidget): 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 @@ -901,19 +925,6 @@ class Multiline(Element, QWidget): def SetFocus(self): pass - def eventFilter(self, widget, event): - if (event.type() == Qt.QEvent.KeyPress and - widget is self.QT_TextEdit): - key = event.key() - if key == Qt.Qt.Key_Escape: - print('escape') - else: - if key == Qt.Qt.Key_Return: - self.QT_TextEdit.setText('return') - elif key == Qt.Qt.Key_Enter: - self.QT_TextEdit.setText('enter') - return True - return QtGui.QWidget.eventFilter(self, widget, event) def __del__(self): super().__del__() @@ -1881,16 +1892,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: @@ -2439,6 +2441,7 @@ class Window: 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): @@ -3215,7 +3218,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: @@ -3253,6 +3257,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: @@ -3527,6 +3533,7 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): # print(style) column_layout = QFormLayout() column_vbox = QVBoxLayout() + PackFormIntoFrame(element, column_layout, toplevel_win) column_vbox.addLayout(column_layout) column_widget.setLayout(column_vbox) @@ -3557,7 +3564,7 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element.Tooltip: element.QT_Label.setToolTip(element.Tooltip) - # element.QT_Label.setMargin(element.Pad[0]) + qt_row_layout.setContentsMargins(element.Pad[0], element.Pad[0], element.Pad[1], element.Pad[1]) qt_row_layout.addWidget(element.QT_Label) # ------------------------- BUTTON element ------------------------- # elif element_type == ELEM_TYPE_BUTTON: @@ -3581,6 +3588,8 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element.Disabled: element.QT_QPushButton.setDisabled(True) + qt_row_layout.setContentsMargins(element.Pad[0], element.Pad[0], element.Pad[1], element.Pad[1]) + qt_row_layout.addWidget(element.QT_QPushButton) # if element.Pad[0] is not None: # element.QT_QPushButton.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) @@ -3607,7 +3616,12 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element_size[1] is not None: element.QT_QLineEdit.setFixedHeight(element_size[1]) # element.QT_QLineEdit.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) - # qt_row_layout.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) + if (element.Focus or toplevel_win.UseDefaultFocus) and not focus_set: + focus_set = True + toplevel_win.FocusElement = element.QT_QLineEdit + + qt_row_layout.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) + qt_row_layout.addWidget(element.QT_QLineEdit) # ------------------------- COMBO BOX (Drop Down) element ------------------------- # elif element_type == ELEM_TYPE_INPUT_COMBO: @@ -3633,8 +3647,8 @@ 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(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) qt_row_layout.addWidget(element.QT_ComboBox) # ------------------------- OPTION MENU (Like ComboBox but different) element ------------------------- # @@ -3659,6 +3673,15 @@ 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(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) @@ -3687,7 +3710,12 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_TextEdit.setPlaceholderText(default_text) qt_row_layout.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) - # element.QT_TextEdit.installEventFilter(element) + 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) @@ -3716,13 +3744,15 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_TextBrowser.insertPlainText(default_text) element.QT_TextBrowser.moveCursor(QtGui.QTextCursor.End) qt_row_layout.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) - qt_row_layout.addWidget(element.QT_TextBrowser) # ------------------------- 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] @@ -3739,7 +3769,6 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): if element_size[1] is not None: element.QT_Checkbox.setFixedHeight(element_size[1]) qt_row_layout.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) - qt_row_layout.addWidget(element.QT_Checkbox) # ------------------------- PROGRESS BAR element ------------------------- # elif element_type == ELEM_TYPE_PROGRESS_BAR: @@ -3768,6 +3797,15 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_Radio_Button.setFixedHeight(element_size[1]) qt_row_layout.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) + 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(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) qt_row_layout.addWidget(element.QT_Radio_Button) # ------------------------- INPUT SPIN Box element ------------------------- # @@ -3790,6 +3828,7 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): element.QT_Spinner.setFixedWidth(element_size[0]) if element_size[1] is not None: element.QT_Spinner.setFixedHeight(element_size[1]) + qt_row_layout.setContentsMargins(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) qt_row_layout.addWidget(element.QT_Spinner) @@ -3860,14 +3899,21 @@ def PackFormIntoFrame(window, containing_frame, toplevel_win): pass # ------------------------- 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(element.Pad[0],element.Pad[0],element.Pad[1], element.Pad[1]) - qt_row_layout.addWidget(element.QT_Slider) # ------------------------- DIAL element ------------------------- # @@ -3958,11 +4004,10 @@ def StartupTK(window): window.QTWindow.setWindowFlags(Qt.FramelessWindowHint) 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() @@ -3970,8 +4015,8 @@ def StartupTK(window): # shadow.setBlurRadius(50) # window.QTWindow.setGraphicsEffect(shadow) - if window.KeepOnTop: - window.QTWindow.setWindowFlags(Qt.WindowStaysOnTopHint) + # if window.KeepOnTop: + # window.QTWindow.setWindowFlags(Qt.WindowStaysOnTopHint) style = '' @@ -4024,6 +4069,8 @@ def StartupTK(window): pass #### RUN MAIN LOOP HERE ##### window.QTWindow.setLayout(window.QT_Box_Layout) + if window.FocusElement is not None: + window.FocusElement.setFocus() window.QTWindow.show() ####### The thing that causes the window to be visible ###### diff --git a/PySimpleGUI_Qt/question.ico b/PySimpleGUI_Qt/question.ico new file mode 100644 index 0000000000000000000000000000000000000000..c5bccb55495ad786497174f359e915209f0acbd6 GIT binary patch literal 43646 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 literal 0 HcmV?d00001