From eff3b977823324f86a15fb89af4b6ac9b8ffd8ea Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy <mike_barnett@hotmail.com> 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