Pysimplegui: Columns within tabbed groups (Duplication) unless using multiple Window declarations (Now tab colors)

Created on 25 Sep 2018  ·  52Comments  ·  Source: PySimpleGUI/PySimpleGUI

Is there a reason that in the tabbed example script there is only a 'form2' window defined?

I have the desired result that I'm looking for here in this code, which produces
multiple columns across tabbed groups. I'm wondering if this is the proper way to
go about it though (rendering two windows).

In your tabbed example you only define a single form window (form2), a single Window which
produces the right results for that script. However if I try the same when adding columns like this, then both layouts show up on tab2 (duplicated), and tab1 has a large blank space at the top of the tab.

import PySimpleGUI as sg

sg.ChangeLookAndFeel('Black')

form = sg.Window('EBay Super Searcher', auto_size_text=False)
form2 = sg.Window('EBay Super Searcher', auto_size_text=False)

column1 = [[sg.Text('Column 1', background_color=sg.DEFAULT_BACKGROUND_COLOR, justification='center', size=(10, 10))]]
column2 = [[sg.Text('Column 2', background_color=sg.DEFAULT_BACKGROUND_COLOR, justification='center', size=(10, 10))]]

layout1 = [[sg.Column(column1, background_color='gray34'), sg.Column(column1, background_color='gray34')]]

layout2 = [[sg.Column(column2, background_color='gray34'), sg.Column(column2, background_color='gray34')]]

sg.ShowTabbedForm('eBay Super Searcher', (form, layout1,'Where To Save'), (form2, layout2, 'Categories & Search String'))

#

Example of duplication:###

#

The original tabbed script also duplicates the tab's display when adding columns like this

import PySimpleGUI as sg

sg.ChangeLookAndFeel('Black')

window=sg.Window('EBay Super Searcher', auto_size_text=True)

form = sg.Window('EBay Super Searcher', auto_size_text=False)

form2 = sg.Window('EBay Super Searcher', auto_size_text=False)

column1 = [[sg.Text('Column 1', background_color=sg.DEFAULT_BACKGROUND_COLOR, justification='center', size=(10, 10))]]
column2 = [[sg.Text('Column 2', background_color=sg.DEFAULT_BACKGROUND_COLOR, justification='center', size=(10, 10))]]

layout1 = [[sg.Column(column1, background_color='gray34'), sg.Column(column1, background_color='gray34')]]

layout2 = [[sg.Column(column2, background_color='gray34'), sg.Column(column2, background_color='gray34')]]

sg.ShowTabbedForm('eBay Super Searcher', (form2, layout1,'Where To Save'), (form2, layout2, 'Categories & Search String'))

All 52 comments

This file needs to be deleted.

This is no longer the way to do tabs... Please don't use it.

Please look at the other 2 tab demo programs for how tabs really work now. Also, the Readme has been updated with a lot of info on tabs.

Ok cool. I guess I'm in the middle of a major shift >.< No problem thanks!

Yes, I wasn't _planning_ on rewriting Tabs from scratch the other day, just like I wasn't planning on supporting 2.7 today. Sometimes sh*t happens.

The thing to remember when in-lining tabs or any other container element, is to make sure it's a list of lists. They should always look like this: [ [ ] ]
I can't tell you HOW many times I've gotten an error about iterators or lists or some other problem because I didn't put enough [ ]'s around stuff.

If you keep in mind that all content look exactly like a Window layout that you'll be fine. In fact, you can easily put a window layout into a tab just by moving your layout variable from the Window call to the Tab call.

I just sliced up our Everything Bagel into tabs by cutting and pasting. I got bit a couple times. Even the TabGroup takes a [ [ ] ] argument.

#!/usr/bin/env Python3
import PySimpleGUI as sg

sg.ChangeLookAndFeel('GreenTan')

# ------ Menu Definition ------ #
menu_def = [['File', ['Open', 'Save', 'Exit', 'Properties']],
            ['Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ],
            ['Help', 'About...'], ]

# ------ Column Definition ------ #
column1 = [[sg.Text('Column 1', background_color='#F7F3EC', 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')]]

tab1 = [
    [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))],]


tab2 = [    [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),
     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='#F7F3EC')]])],]


layout = [
    [sg.Menu(menu_def, tearoff=True)],
    [sg.Text('All graphic widgets in one form!', size=(30, 1), justification='center', font=("Helvetica", 25), relief=sg.RELIEF_RIDGE)],
    [sg.TabGroup([[sg.Tab('Tab 1', tab1), sg.Tab('Tab 2', tab2)]])],

    [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.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)

button, values = window.Read()

sg.Popup('Title',
         'The results of the window.',
         'The button clicked was "{}"'.format(button),
         'The values are', values)

Closing this one... if you have trouble using the new tab method, let me know...

Thanks for the details. I'll be working on this tomorrow ;)

Aah ok for the outcome I wanted I needed to define two Windows (To have tabbed groups not duplicate what was in the previous window in the subsequent tabs). Let me know if this is good practice :)

import PySimpleGUI as sg

sg.ChangeLookAndFeel('Black')

form1 = sg.Window('EBay Super Searcher', auto_size_text=False)
form2 = sg.Window('EBay Super Searcher', auto_size_text=False)

column1 = [[sg.T('This is inside tab 1')]]
column2 = [[sg.T('This is inside tab 2')]]
column3 = [[sg.T('This is inside tab 3')]]
column4 = [[sg.T('This is inside tab 4')]]

layout1 = [[sg.TabGroup([[sg.Tab('Tab 1', column1), sg.Tab('Tab 2', column2)]])],
[sg.RButton('Read')]]

layout2 = [[sg.TabGroup([[sg.Tab('Tab 3', column3), sg.Tab('Tab 4', column4)]])],
[sg.RButton('Read')]]

sg.ShowTabbedForm('eBay Super Searcher', (form1, layout1,'Where To Save'), (form2, layout2, 'Categories & Search String'))

You must never call ShowTabbedForm again!

This is the old way... I must remove that function, clearly.

Take a look at my example.

You will call
Window().Layout()
and
window.Read()

Do not create two Window objects.

You will create a single Window for now on. You're only showing 1 Window, right? It just happens to have tabs in it.

I'm trying to rework your example now.

Aah ok I think I was working off an old version of the docs. I see the light now!

Damn, I'm struggling to rework what you have.

It's better to look at the newest docs, and Cookbook, and go from there... .that sound like a plan?

Feel free to post again of course if you get stuck.

Start here:

import PySimpleGUI as sg

tab1_layout =  [[sg.T('This is inside tab 1')]]

tab2_layout = [[sg.T('This is inside tab 2')],
               [sg.In(key='in')]]

layout = [[sg.TabGroup([[sg.Tab('Tab 1', tab1_layout), sg.Tab('Tab 2', tab2_layout)]])],
          [sg.RButton('Read')]]

window = sg.Window('My window with tabs', default_element_size=(12,1)).Layout(layout)

while True:
    b, v = window.Read()
    print(b,v)
    if b is None:           # always,  always give a way out!
        break

Do you understand that a Tab IS a column by definition?

Just like a Window is a column, but you don't declare a column.

So, you do not need a column within a tab unless you would have normally needed a column if it were a Window.

Try it without using a column Element and post back here again.

Yes, the column is designated for content arrangement beyond the tab for content across the GUI (custom sizing). I'm not just calling column for no reason. This is just a stripped example so I see where you're coming from.

Ah, got it... just wanted to save you some pain 👍

It's possible to stick everything inside of a column... in fact, it's one way to get a scrollable window.

I can't wait to see the monster you come up with!!

I'm only now realizing the power of tabs. It means you can easily double the real estate for a window for example. Or, add the print output to the GUI itself by making another tab with an output element... .I want to make a demo showing this as it's a great alternative to the Debug Print. Someone already asked if this is possible.

I'm going to reopen until you get to a point where you're happy with the tabs.

Right immediately I had a vision for a fairly organized but complex GUI and this ability helps compartmentalize a lot of content within the same window and keeps the overall GUI size down! I'll post back here with the results once it's ready since you're interested.

Here is my GUI so far. However there is a strange issue where when I load a graph anywhere, all of the text elements within the Window disappear. I'm lead to believe it happens when I add a call to TKCanvas and then it's requirement - Finalize(). Doesn't seem like any of the canvas or graphing img is flooding the Window either. I can open another issue if that makes more sense to you.

Looking at your Matplot examples, they do the same thing. Am I missing an update again? I just updated to the latest but perhaps I missed a new doc somewhere.

#
### GUI
#

def NightMode():
sg.ChangeLookAndFeel('Black')

#
Matplot
#

fig = GenerateGraph()
figure_x, figure_y, figure_w, figure_h = fig.bbox.bounds

#
# Columns
#

col1 = [[sg.Text('App Name:'), sg.InputText('App')],
[sg.Text('Tester Name:'), sg.InputText('Leonardo')],
[sg.Text('Browser Type (IE):'), sg.InputText('FF')],
[sg.T("")],
[sg.Checkbox('Enable MySQLDatabase')],
[sg.Checkbox('PDF Generation', default = True)],
[sg.Checkbox('HTML Generation', default = True)],
[sg.Checkbox('Graph Generation')],
[sg.Checkbox('Email Reporting')]]

col2 = [[sg.T("These settings are for your Topanga MySQL Database.")],
[sg.T("")],
[sg.Text('Topanga MySQL Database IP:'), sg.InputText('')],
[sg.Text('Topanga MySQL Username:'), sg.InputText('')],
[sg.Text('Topanga MySQL Password:'), sg.InputText('')],
[sg.Text('Topanga MySQL Port:'), sg.InputText('')],
[sg.Text('')],
[sg.ReadButton(" Connect Database ")]]

col3 = [[sg.T("These settings should be customized to your apps needs.")],
[sg.T("")],
[sg.Text('User URL:'), sg.InputText('https://')],
[sg.Text('Admin URL:'), sg.InputText('https://')],
[sg.Text('Patient Last Name:'), sg.InputText('')],
[sg.Text('Patient Last Four SSN:'), sg.InputText('')],
[sg.Text('Patient Other Reason for Visit:'), sg.InputText('My gravity is wearing off')],
[sg.T("")],
[sg.Text('Oracle Login Name:'), sg.InputText('')],
[sg.Text('Oracle Login Password:'), sg.InputText('')],
[sg.Text('Database IP Address:'), sg.InputText('')],
[sg.Text('Database Port:'), sg.InputText('')]]

col4 = [[sg.T("Graphing")]]

colm = [[sg.T("")],[sg.T('Topanga ∩(^-^)∩ Test Anatomy', justification="center"),
sg.ReadButton("Create a Topanga")]]

col5 = [[sg.T("Screenshots")]]

col6 = [[sg.T("Reporting")]]

col7 = [[sg.T('Logging')]]

col8 = [[sg.T('Notes:'), sg.InputText('')]]

#
### Tabs
#

tab1 = [[sg.T("General Settings", text_color='blue')],
[sg.Column(col1)]]

tab2 = [[sg.T("Database Settings", text_color='blue')],
[sg.Column(col2)]]

tab3 = [[sg.T("Custom Settings", text_color='blue')],
[sg.Column(col3)]]

tab4 = [[sg.T("Graphing", text_color='blue')],
[sg.Column(col4, pad=(0, (0, 0)))], [sg.Canvas(size=(figure_w, figure_h), key='canvas')]]

tab5 = [[sg.T("Screenshots", text_color='blue')],
[sg.Column(col5)]]

tab6 = [[sg.T("Reporting", text_color='blue')],
[sg.Column(col6)]]

tab7 = [[sg.T("Logging", text_color='blue')],
[sg.Column(col7)]]

tab8 = [[sg.T("Notes", text_color='blue')],
[sg.Column(col8)]]

layout = [[sg.TabGroup([[sg.Tab('General Settings', tab1),
sg.Tab('Database Settings', tab2),
sg.Tab('Custom App Settings', tab3)]])],
[sg.Column(colm)],
[sg.Text('')],
[sg.TabGroup([[sg.Tab('Graphing', tab4),
sg.Tab('Screenshots', tab5),
sg.Tab('Reporting', tab6),
sg.Tab('Logging', tab7),
sg.Tab('Notes', tab8)]])],
[sg.Text('')]]

window = sg.Window("Topanga ∩(^-^)∩", grab_anywhere=False, auto_size_text=False).Layout(layout).Finalize()

fig = GenerateGraph()

fig_photo = Graphing(window.FindElement('canvas').TKCanvas, fig)

while True:
b, v = window.Read()
print(b,v)
if b is None:
break

The two functions are below:

def Graphing(canvas, figure, loc=(0, 0)):

figure_canvas_agg = FigureCanvasAgg(figure)
figure_canvas_agg.draw()
figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
figure_w, figure_h = int(500), int(300)
photo = Tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)

canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)
tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)

return photo

def GenerateGraph():
np.random.seed(19680801)

y = np.random.normal(loc=0.5, scale=0.4, size=200)
y = y[(y > 0) & (y < 1)]
y.sort()
x = np.arange(len(y))

plt.figure(1)

plt.subplot(221)
plt.plot(x, y)
plt.yscale('linear')
plt.title('linear')
plt.grid(True)

plt.gca().yaxis.set_minor_formatter(NullFormatter())
plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
               wspace=0.35)

fig = plt.gcf()

return fig

Your code lost all of the indents.

You have to format as code or all will get lost.

How about posting the code somewhere?

I just noticed that none of your posts have the code formatted.

You have to highlight the code the click the <>

None of your imports are included

Updated. It's a large application already which i can't share all of in it's current state (specific to a certain test case) so grabbing chunks from 5 locations becomes troublesome -_-

Still not running...

No import of PySimpleGUI... even when that's added still other errors... GenerateGraph missing is next one

When you get your gist to run, let me know. Until then I can't really go through and keep trying to patch it up. I'm trying to but there's just too many problems.

I understand the problem you face of trying to segment out just the GUI part.

You can't duplicate the GUI problems without all the actual drawing?

I removed the Graph element and it's running now

Aah sorry to do that to you. It's updated and verified running with functions in the right place. I kind of figured you'd just read the code and it was something simple I missed but yes my mistake.

Do you see text in the GUI with the graph loaded? For me, I see no text with that as well as the other matplot examples.

The Matplotlib examples are not working either?

I see missing text. I'm going through removing all of the graphing code to see if the basic gui runs with no issues, etc

Process of elimination.

It's going to take a while.

It would be nice to narrow it down... a lot further to an example where there's a definitive "it works without this and this breaks it"

It's an impressively LARGE GUI

It would have been a lot easier on both of us if we had realized the Demo_Matplotlib.py didn't work.

Haha indeed. I just eliminated code from your Demo_Matplotlib.py and it seems that the text has issues until you comment out plt.figure(1) and possibly plt.gcf() - I think they both produce the issue. It's not Finalize() nor draw_figure()

It's not in MY code, of course. lol

Disclosure if it's not obvious... I don't know what the f*ck I'm doing with any of those Matplotlib examples. I lifted 100% of the code.

I see now also that the browser crashes on one of the examples.

I would like to know if that example ever worked.

DOH! The browser broke because SOMEONE renamed:
reference_transform to reference_transwindow

whew, relieved nothing major there... but still doesn't answer the problem of the disappearing text.

Have you done any searches for tklinter label and matplotlib? I recall a number of issues while doing research

Yes definitely. With this API on top of tkinter I'm not exactly sure how to patch it up though. I suppose I could go into the source >.<

Interestingly, if you create a second layout, and declare a second Window, it displays correctly in the second Window: https://gist.github.com/eagleEggs/2acca6dbce4762aaadc60d3048f69266

I tested all the way back to 2.11.0 and it didn't work.

Best I can tell it's NEVER worked.

There's no need for you to get into the PSG source code. This is not a PSG problem that I see... it looks like a tkinter / matplotlib integration issue is why I asked.

ah, wait... no text in a window... that sounds familiar

That's a "root" window issue

Finalize is a way to get tkinter to layout and "draw" the window. You can't call it twice. What you're actually doing is fooling PSG into using a different root window.

It's getting late and I can't debug any further. I'll look at it tomorrow for sure now that I know what to look for... thanks for helping!! It really helped narrow down the problem

Let's debug further via email... email me at [email protected]

Sure no problem! Thanks for great support of your library and hopefully my abuse of it will help weed out some bugs and help it grow.

Wait, I can't stand to leave anyone hanging.... If I don't get you going I won't be able to sleep...

add a line of code at line 3474 (that's what it is in my code at the moment)

def StartupTK(my_flex_form):
    global _my_windows

    ow = _my_windows.NumOpenWindows
    # print('Starting TK open Windows = {}'.format(ow))

add 1 line of code so it reads:

def StartupTK(my_flex_form):
    global _my_windows

    ow = _my_windows.NumOpenWindows
    ow = 1
    # print('Starting TK open Windows = {}'.format(ow))

Are you operational now?

That should have fixed it... at least for now... your program should be running fine. I now have to figure out a way to force this for the Matplotlib programs.

Making the change now! :)

YES! Beautiful :D Now get some sleep. Thanks!

OK... one last change...

Grab the latest from Master Branch.

Set a flag when you create the Window:
force_toplevel = True

This will do what we did manually. Basically when we're doing these matplotlib windows, for whatever reason they like to be a toplevel window, not a Tk window.

This will get you running for a while so that you can run with production code instead of a hacked version.

Great, I was worried about how that would be managed manually with new builds but you got it covered. Thanks again!

I tested this under Linux and the missing Text also happens. So it's not Windows specific, which is a good thing. At least there's a workaround / hack to get past it.

Working great and stable. Feel free to close unless you want to leave open to potential further changes and/or discussion. Thanks!

How on earth do we document this thing?? I might as well have called it magic_matplotlib_flag

If you're using Matplotlib, be sure and get the magic flag

Hahaha. You're basically right though - You could give the impression it's less convoluted and reassign the variable in the function to plot from force_toplevel

So essentially something like
window = sg.Window("Best Window", grab_anywhere=False, auto_size_text=False, plot=True).Layout(layout).Finalize()

But you may eventually have another bug, or someone that needs to actually assign force_toplevel - At which point just letting that flag fly may be the most flexible.

Yea, I named it in a generic way thinking someone else will want to do the same thing some day. BTW, you don't need to type:
grab_anywhere=False
anymore... I caved to public pressure. I forgot to do it on all the popups and just now checked those in. I'll probably release to PyPI soon since I forgot all those popups on my last attempt.

Matplot 3.0 was just released.

I upgraded to it wondering if perhaps that force_toplevel hack would be no longer needed. And.... same problem... missing text if you don't use the force_toplevel hack.

Man that would have been great

Closing this one....

Was this page helpful?
0 / 5 - 0 ratings

Related issues

xuguojun168 picture xuguojun168  ·  3Comments

flowerbug picture flowerbug  ·  4Comments

lucasea777 picture lucasea777  ·  3Comments

ihouses picture ihouses  ·  6Comments

ECOM-Klaus picture ECOM-Klaus  ·  4Comments