#๐Ÿ”’ what is pyside6 how do i use it?

244 messages ยท Page 1 of 1 (latest)

prime beacon
#

i see that there is the designer
but how will i auto add more buttons to the sidebar?
whats the diff between Layout frame and groupbox which should i use how do i make an area where i can draw freely ect. ?
any1 knows good tutorials or something?

astral wyvernBOT
#

@prime beacon

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

prime beacon
normal snow
#

pyside doesn't necessarily have the same concept as pack/grid that tkinter does

#

it uses layouts to hold widgets

#

QVBoxLayout will arrange things vertically. QHBoxLayout will arrange things horizontally

#

there's also QGridLayout if you want a grid of things

#

The equivalent of a tkinter frame would be QWidget

#

it's just an empty container widget that can have its own layout

prime beacon
#

how do i import it? pyside is not found

normal snow
#

there's also QFrame which is similar, but it has extra styling options (you can emboss, draw boxes around it, etc)

#

then QGroupBox is just a widget that has a box around it and a label

normal snow
#

there's 3 libraries you'll usually want to import

#

from PySide6 import QtWidgets, QtGui, QtCore

#

QtWidgets is where you can find all your widgets. Buttons, sliders, checkboxes, frames, etc

#

QtGui is more about widget modification. Things like fonts, colours, painting tools, etc

prime beacon
#

i didnt think capital matters thats the problem

normal snow
#

QtCore is helped classes. Things like measuring sizes, getting the cursor

normal snow
#
from PySide6 import QtWidgets, QtCore, QtGui


class Window(QtWidgets.QDialog):
    pass



app = QtWidgets.QApplication()
win = Window()
win.show()
app.exec()
#

this is the basic setup for a pyside ui

#

you always need to initialize your QApplication first

#

then create whatever you want to display

#

then exec your application

prime beacon
#

ok so as a start how do i make my menu?
it has 2 labels and 2 buttons centered and lined up verticaly

normal snow
#

can you show what that looks like?

prime beacon
#

lower 2 are the buttons

normal snow
#

ok, so you can see that everything here is arranged vertically, so a QVBoxLayout would be perfect

prime beacon
#

also spacings with the buttons and teexts haveing bigger spacing

normal snow
#

but we also know that this will all need to removed after a selection is made, so we'll likely want to make it into its own widget (like a tkinter frame)

#

we can make this an entirely separate class if we want (and I recommend it)

#

class WelcomeMenu(QtWidgets.QWidget):

prime beacon
normal snow
#
class WelcomeMenu(QtWidgets.QWidget):

    def __init__(self):
        super().__init__()

        main_layout = QtWidgets.QVBoxLayout()
        self.setLayout(main_layout)
#

here's the basic setup for a custom qwidget

#

we create a layout, then set it to self

#

now we can start adding widgets to that layout

#
class WelcomeMenu(QtWidgets.QWidget):

    def __init__(self):
        super().__init__()

        main_layout = QtWidgets.QVBoxLayout()
        self.setLayout(main_layout)

        title_label = QtWidgets.QLabel("Welcome!")
        subtitle_label = QtWidgets.Label("Select an option")

        option_1_btn = QtWidgets.QPushButton("Option 1")
        option_2_btn = QtWidgets.QPushButton("Option 2")
#

I always start by making the widgets that I know I'll need

#

then we can add them to the layout in the order that we want to display them

prime beacon
normal snow
#
main_layout.addWidget(title_label)
main_layout.addWidget(subtitle_label)
main_layout.addWidget(option_1_btn)
main_layout.addWidget(option_2_btn)
normal snow
#

it's what actually tells the layout to display that widget

#

so this is nearly done, but you now need to add this widget to the main window

#
class Window(QtWidgets.QDialog):
    
    def __init__(self):
        super().__init__()
        main_layout = QtWidgets.QVBoxLayout()
        self.setLayout(main_layout)
#

we do basically the same thing for the Window class

#

a QDialog is basically a QWidget that acts as a window

#
self.welcome_menu_widget = WelcomeMenu()
main_layout.addWidget(self.welcome_menu_widget)
#

then all we need to do is make an instance of our WelcomeMenu and add it

#

I used an attribute for the welcome menu widget because we know we'll need to access it later to remove it

#

so it's not very exciting yet, but everything is in place!

#

whoops, they both say option 1

#

to set the command of a button like you would in tkinter, pyside uses something called "signals and slots"

prime beacon
#

why do i need a box why cant i put it straight into the window?

normal snow
#

Because once you click a button, you want to remove all of these and display something else, yes?

normal snow
#

It's a lot easier to remove one widget than it is all of the ones you put into your menu

prime beacon
#

an other premade widget containing the main functionalyty

normal snow
#

also having it as a separate class makes your code less cluttered

#

we can focus on all of our main menu logic and styling in the main menu class

#

and when we want to remove the menu, we only need to remove that one widget

prime beacon
#

when the menu already is a layout

normal snow
#

layouts are what hold our widgets

#

even though we're only displaying 1 thing

#

we still want a layout

#

plus we'll reuse that layout for other things once we remove the menu

prime beacon
normal snow
#

ahh yeah, main_layout should be an attr

prime beacon
#
from PySide6 import QtWidgets, QtCore, QtGui
from Menu.UI.menu_ui import Menu

class Window(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.main_layout = QtWidgets.QVBoxLayout()
        self.setLayout(self.main_layout)

        self.menu = Menu()
        self.main_layout.addWidget(self.menu)


app = QtWidgets.QApplication()
win = Window()
win.show()
app.exec()

why does this instantly exit with no error?

normal snow
#

what's in your Menu file?

prime beacon
#
from PySide6 import QtWidgets, QtCore, QtGui


class Menu(QtWidgets.QWidget):
    def __init__(self) -> None:
        super().__init__()
        
        main_layout = QtWidgets.QVBoxLayout()
        self.setLayout(main_layout)

        title_label = QtWidgets.QLabel("State Machine Simulator!")
        subtitle_label = QtWidgets.QLabel("Select an option")

        option_1_btn = QtWidgets.QPushButton("Undeterministic Finite Incomplete State Machine")
        option_2_btn = QtWidgets.QPushButton("Deterministic Finite Incomplete Stack State Machine")

        main_layout.addWidget(title_label)
        main_layout.addWidget(subtitle_label)
        main_layout.addWidget(option_1_btn)
        main_layout.addWidget(option_2_btn)
normal snow
#

it runs for me if I use that class

prime beacon
#

ah i may have failed to save something it works now

#

how do i center all of the widgets in the menu?

normal snow
#

ok so layout alignment takes a bit of getting used to

#

but it's pretty straightforward once you understand a few quirks

prime beacon
#

also i want them to be pushed together in the midle

normal snow
#

so by default, a layout will evenly space its widgets within

normal snow
#

we can see how this expands

#

certain widgets have certain "size policies"

prime beacon
#

wait why the buttons stick?

normal snow
#

buttons may expand horizontal, but not vertical

#

because QLabel has a size policy that allows for vertical resizing, they get priority

#

that causes the buttons to "stick"

#

we could change this in a number of ways

#

we could tell buttons to resize vertically

#

we could tell labels to stop resizing vertically

prime beacon
normal snow
#

each widget is technically placed inside a "QLayoutItem" before being added to the layout

#

if the widget is not capable of resizing, the qlayoutitem will resize instead

#

but again it depends on "size hints" and "size policy"

#

honestly a lot of this isn't stuff you need to worry about for the moment, there's some simple workarounds

#

the simplest one is to use a "stretcher"

#

it has high priority and automatically fills out the space

#

we can add a stretcher before and after we add our widgets

#

it will give us this result as the window scales up vertically

#
main_layout.addStretch()
main_layout.addWidget(title_label)
main_layout.addWidget(subtitle_label)
main_layout.addWidget(option_1_btn)
main_layout.addWidget(option_2_btn)
main_layout.addStretch()
prime beacon
normal snow
#

if we display a box around our qlabel, we can see what's actually happening

prime beacon
#

oh

normal snow
#

we can easily set an alignment on our label though

#

title_label.setAlignment(QtCore.Qt.AlignCenter)

prime beacon
#

also the buttons streching looks bad how do i change that size policy or how does that work?

normal snow
#

ok so every widget has what's called a "Size Hint"

#

this is what the widget determines how much space it should take up

#

the size policy has a relationship with the size hint

#

it might be a bit counter intuitive

#

but a size policy of "Minimum" tells a widget that its size hint is the smallest size that the widget is allowed to be

prime beacon
normal snow
prime beacon
#

yes

normal snow
#

it absolutely should exist. Check your capitalization

prime beacon
#

the AlingmentCenter is the part thats missing

normal snow
#

it's AlignCenter

#

not AlignmentCenter

prime beacon
normal snow
#

ahh

#

yeah, sometimes autocomplete for the Qt enum isn't great

#

not sure why tbh

normal snow
#

works fine in pycharm though

#

sublime gives me trouble with it

shrewd relic
normal snow
#

ok, so to actually set the size policy, we just use setSizePolicy

prime beacon
normal snow
#

you wrote Aling

#

it's Align

#
option_1_btn.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
#

This is how we set the size policy

#

we have to give it two values. The first one is the policy for horizontal and the 2nd is for vertical

#

Fixed is the easiest option for now. It basically says "Use the size hint" and never grow or shrink

#

you'll need to center it though

#

To center just a specific widget in a layout, you can set the alignment during addWidget

#

now everything is nice and centered

#

you could actually change this to inherit from QFrame instead of QWidget which would give you some style options

#

now it has a nice box

prime beacon
normal snow
#
main_layout.addStretch()
main_layout.addWidget(title_label)
main_layout.addWidget(subtitle_label)
main_layout.addWidget(option_1_btn, alignment=QtCore.Qt.AlignCenter)
main_layout.addWidget(option_2_btn, alignment=QtCore.Qt.AlignCenter)
main_layout.addStretch()
#

pretty much all alignment options accept the Qt.Align flags

#

There's a ton of useful enums in Qt, so most people will actually do from PySide6.QtCore import Qt

prime beacon
#

i got something to do but than ill come back and next is the Undeterministic machines ui

normal snow
prime beacon
#

next i want to make something like this
i seen theres a scrollable widget so i want that and changing amount of buttons in it
i want a big are where i can detect clicks to the right + i may want to add some text on the drawing are at random spots

prime beacon
# normal snow

what widget should i use for my drawing area?
and how do i make it so the earlyet 2 buttons are at the bottom but the rest at the top of the scroll box?

#

what is that scrollbox called?

normal snow
normal snow
#

It's a bit weird though

#

you have to set a widget to it

#

and that widget should have a layout

prime beacon
normal snow
#

so there's always like an "add new" button kinda thing?

normal snow
#

instead of addWidget, you can insertWidget instead

prime beacon
#

but maybe ill auto add it when the user uses a new letter

normal snow
#

so that you can insert new widgets above the "add new" button

#
from PySide6 import QtWidgets, QtCore, QtGui

class Window(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.main_layout = QtWidgets.QVBoxLayout()
        self.setLayout(self.main_layout)

        add_new_btn = QtWidgets.QPushButton("Add New +")

        self.main_layout.addWidget(add_new_btn)

        add_new_btn.clicked.connect(self.on_add_new_clicked)

    def on_add_new_clicked(self):
        btn = QtWidgets.QPushButton("Button")
        self.main_layout.insertWidget(self.main_layout.count() - 1, btn)


app = QtWidgets.QApplication()
win = Window()
win.show()
app.exec()
#

try running this code

#

you'll notice that all the widgets end up spaced though

#

you can set the entire alignment of the layout to AlignTop though to solve this

prime beacon
#

nice

#

ill need this for the other one too so ill add it in a sep folder

normal snow
#

once you set up your scroll area, you'll likely end up with something like this which makes it a bit of a pain to see the Add New btn

#

I would have it outside of the scroll area

#

I actually have my own similar UI

#

It's a pretty small button, but I use the + in the corner to add new buttons (or new tabs)

#

but I can also right click in any empty spot in the scroll area to add new buttons too

#

it just gives me a menu popup like this

prime beacon
#

i think the popup is too much for me bc all u need is to name it

#

does the scroll area also need a layout?

normal snow
#

the scroll area needs a widget, and that widget needs a scroll area

#

it can get a bit confusing with this one

#
self.btn_scroll_area = QtWidgets.QScrollArea()
self.btn_scroll_area.setWidgetResizable(True)

self.btn_scroll_wdg = QtWidgets.QWidget()

self.btn_scroll_layout = QtWidgets.QVBoxLayout()

self.btn_scroll_layout.setAlignment(QtCore.Qt.AlignTop)
self.btn_scroll_wdg.setLayout(self.btn_scroll_layout)
self.btn_scroll_area.setWidget(self.btn_scroll_wdg)

self.main_layout.addWidget(self.btn_scroll_area)
#

this is what I used

prime beacon
normal snow
#

the widget is a single widget that the scroll area will display

#

so if you had a giant image for example, you could display that in a scroll area

#

this is fine if I just want to display a single widget

#

but if I want to display a widget that also contains other widgets, then I need something that can hold a layout

#
self.scroll = QtWidgets.QScrollArea()
img = QtWidgets.QLabel()
img.setPixmap(QtGui.QPixmap('mountains.png'))
self.scroll.setWidget(img)
normal snow
#

I create a widget

#

I create a layout

#

I setLayout that layout to that widget

#

then I setWidget that new widget to the scroll area

prime beacon
#

how do i remove the last widget added to a layout?

normal snow
#

It depends. Usually I just destroy the widget and the layout will clean it self up

#

widget.deleteLater()

prime beacon
#

but i may want to keep the same instance of the menu and other parts

normal snow
#

You can hide the visibility of a widget then with setVisibility

prime beacon
#

whats the diff between an item and a widget?

normal snow
#

Layouts and some other widgets display their data as items

#

QListWidget for example displays items

#

Even though you addWidget to a layout, it ultimately creates layout items

prime beacon
#

why is this?

normal snow
#

You're missing clicked

#

wdg.clicked.connect

prime beacon
prime beacon
normal snow
#

Yes

#

Another option is using a Stack Layout instead

#

It's useful when you want different screens to display to a user

prime beacon
#

why doesnt my scroll apier?

class Undet(QtWidgets.QFrame):
    def __init__(self, master) -> None:
        super().__init__()
        
        self.master = master

        main_layout = QtWidgets.QHBoxLayout()
        self.setLayout(main_layout)

        self.sandbox = QtWidgets
        self.scrollbox = buttons_scroll()

        main_layout.addWidget(self.scrollbox)
#
class buttons_scroll(QtWidgets.QScrollArea):
    def __init__(self) -> None:
        super().__init__()

        self.setWidgetResizable(True)
        self.area = QtWidgets.QWidget()
        self.area_layout = QtWidgets.QVBoxLayout()
        self.area_layout.setAlignment(QtCore.Qt.AlignTop)

        self.add_new = QtWidgets.QPushButton(text=" + ")
        self.add_new.clicked.connect(lambda: self.add_letter("x"))
        self.buttons: list[QtWidgets.QPushButton] = []

        self.area_layout.addWidget(self.add_new)

    def add_letter(self, letter: str) -> None:
        self.buttons.append(QtWidgets.QPushButton(text=letter))
        self.buttons[-1].clicked.connect(self.make_callback(letter))
        self.area_layout.insertWidget()
    
    def make_callback(self, letter: str):
        def callback():
            print(letter)
        return callback
normal snow
#

Can you send the full code?

prime beacon
#

main

from PySide6 import QtWidgets, QtCore, QtGui
from Menu.UI.menu_ui import Menu
from Undet.UI.undet_ui import Undet

class Window(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.main_layout = QtWidgets.QVBoxLayout()
        self.setLayout(self.main_layout)

        self.menu = Menu(self)
        self.undet = Undet(self)
        self.main_layout.addWidget(self.menu)

    def Undet(self):
        self.main_layout.removeItem(self.main_layout.itemAt(0))
        self.main_layout.addWidget(self.undet)

app = QtWidgets.QApplication()
win = Window()
win.show()
app.exec()
#
class Menu(QtWidgets.QFrame):
    def __init__(self, master) -> None:
        super().__init__()

        self.master = master

        main_layout = QtWidgets.QVBoxLayout()
        self.setLayout(main_layout)

        title_label = QtWidgets.QLabel("State Machine Simulator!")
        title_label.setAlignment(QtCore.Qt.AlignCenter)
        subtitle_label = QtWidgets.QLabel("Select an option")
        subtitle_label.setAlignment(QtCore.Qt.AlignCenter)

        option_1_btn = QtWidgets.QPushButton("Undeterministic Finite Incomplete State Machine")
        option_1_btn.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        option_1_btn.clicked.connect(master.Undet)
        option_2_btn = QtWidgets.QPushButton("Deterministic Finite Incomplete Stack State Machine")
        option_2_btn.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)

        main_layout.addStretch()
        main_layout.addWidget(title_label)
        main_layout.addWidget(subtitle_label)
        main_layout.addWidget(option_1_btn, alignment=QtCore.Qt.AlignCenter)
        main_layout.addWidget(option_2_btn, alignment=QtCore.Qt.AlignCenter)
        main_layout.addStretch()
```menu
normal snow
#

you never did self.setWidget(self.area) in your buttons_scroll class

#

make sure you name classes with capitalization or it can be hard to follow

#

class ButtonsScroll

#
def make_callback(self, letter: str):
    def callback():
        print(letter)
    return callback
#

this is also unnecessary

#

just call the method directly

#

ahh, it has an argument

#

use partial

#

from functools import partial

#
self.buttons[-1].clicked.connect(partial(letter, self.on_btn_clicked))
prime beacon
astral wyvernBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.