#π Creating a context menu in PySide6
609 messages Β· Page 1 of 1 (latest)
@sleek parcel
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.

I thought we're switching to tree widget?
i mean we can do
i tried having a look at it earlier in college
but it confused me
ok, let's start from a blank file
make a class for your main window and add a layout and tree widget
all my work? gone π₯Ή
that's how you practice π
Surely you can make a class for a simple window by now, right?
sometimes when u got a confusing mess its better to just hit the delete button rather than getting 7 headaches trying to fix it
I find it's more of "If you spend a few days building something, you have to refresh yourself on how you even got started"
you can keep it for later, sure
okay
if it was a few days or longer than sure thats a valid point 
@sleek parcel do you think you can write a class for a basic window yet without looking at your old code?
I'll brb in a few minutes actually while you do that
yes im just making a new file
ok I'm back
eh i got a little bit done
import sys
from PySide6 import QtWidgets, QtGui, QtCore
class MainWindow(QtWidgets.QMainWindow):
DEFAULT_SIZE = (500, 500)
def __init__(self):
pass
window = MainWindow()
app = window.show()
thats as much as i can remember
That's pretty good but needs a few corrections
We need to create our application
app = QtWidgets.QApplication()
you can put sys.argv in there but I never bother
To run things, it's pretty much always
Create application
Create widget you want to display
Show widget
Start application
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec()
okay
We're also missing the super() call inside init, and we're going to be using QDialog instead of QMainWindow
but everything else is good π
from PySide6 import QtWidgets, QtGui, QtCore
class MainWindow(QtWidgets.QDiaglog):
DEFAULT_SIZE = (500, 500)
def __init__(self):
super().__init__()
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec()
Dialog, not Diaglog
Always run to test
from PySide6.QtCore import Qt
I also include this import
since we'll need it for some things later
okay done
Also just to make sure you know what we're building, we're going to make something like this
okay cool
before we were working with a QListWidget
which is a container widget that displays QListWidgetItems
Now we're going to be making a QTreeWidget
want to guess what it displays?
QTreeWidgetItem
don't worry about view
Widgets and Views are somewhat different
QTreeWidget and QTreeView work differently
okay i see
View took me a lot longer to wrap my head around
but Widget isn't too bad
there's Widget/View for List, Tree, and Table
how do i set the window size again
self.resize
or self.setFixedSize if you don't want to be able to change it after (but I think we just want resize here)
its saying my default size doesnt exist
it would be MainWindow.DEFAULT_SIZE
Ok, so I want you to see an example of a more complex QTreeWidget, since it will explain some of the behaviours we have to work around
QListWidget is only ever a single column
but QTreeWidget can have multiple columns
Here's an example of one I have open from work (I didn't make this one though)
It displays parts of a 3d character skeleton
so we have 2 new features in QTreeWidget
3 columns
we can have nested items
and we have columns
We still only really care about have 1 column
but we want the nested items
Unfortunately though, whenever we do something with the tree widget, we have to tell it what column we're working with
so it's just an extra thing to give it "column 0" for most methods
we could have 2 columns, another saying if the task is completed or not
That's a good idea! We can start with 1 for now with the checkbox, and add that later if we want
sure we could but isnt that what the radio boxes to the left of task here are for ? 
Ok, let's just make our tree so we can play around with it
yes lets do that, removes the need for a history tab
okay so what now
We don't have a layout yet, so we want to start with that
main_layout = QtWidgets.QVBoxLayout(self)
since this is our top-most layout, we can just use self in the argument
why do i need to pass self into that
self is our window
okay
and layouts can accept a parameter for its parent
oh i see
we don't usually parent layouts this way
except for the top-most one
all other layouts, we would use addLayout
ok, now we have to create our tree widget, and add it to the layout
this process is basically identical for ANY widget
let me try
class MainWindow(QtWidgets.QDialog):
DEFAULT_SIZE = (300, 325)
def __init__(self):
super().__init__()
self.resize(*MainWindow.DEFAULT_SIZE)
self.setWindowTitle("Todo List")
main_layout = QtWidgets.QVboxLayout(self)
self.tree_list_widget = QtWidgets.QTreeWidget()
main_layout.add(self.tree_list_widget)
okay i changed the name of mine to that
Name your widgets about what they represent, not what kind of widget they are
adding a widget is with addWidget, not add
they're all addSomething
i also fixed that
addWidget
addLayout
addSpacing
addStretch
now it just shows me a line with 1 in the column
Yup, by default, tree widgets have 1 column, and it's labeled 1
You can decide if we want to rename the header, or just remove it
I might keep the header but rename it
We can rename it Tasks if you like
perfect π
that works for a single column
we can use a list of headers if we have multiple columns
but then we use setHeaderLabels
Ok, so before, working with QListWidget, you would create a QListWidgetItem and add it to the QListWidget
yep
tree is slightly different
When we make a QTreeWidgetItem, we have a choice of where to add it
we can add it directly to the widget
or we can add it as a child of another item
exactly π
Also when we create an item, we have to give it a list instead instead of a string. The list represents the columns
Since we're only working with 1 column, we'll always give it a list of 1 string
a bit annoying, but that's how it works
let's make an example item
then we can refactor it into a method
okay ill try first
def __init__(self):
super().__init__()
self.resize(*MainWindow.DEFAULT_SIZE)
self.setWindowTitle("Todo List")
main_layout = QtWidgets.QVBoxLayout(self)
self.todo_wgt = QtWidgets.QTreeWidget()
self.todo_wgt.setHeaderLabel("Tasks")
top_task = QtWidgets.QTreeWidgetItem(["Quadratics Homework"])
self.todo_wgt.addTopLevelItem(top_task)
main_layout.addWidget(self.todo_wgt)
it looks a bit ugly but works
yeah perfect
because you only have 1 item
do u want to set a timeout on the highlight ?
yeah, unfortunately you can't really "click off" an item in these widgets
(without extra logic)
maybe later yes
ok, now try and add another item, but this time as a child in the Quadratics Homework
will do
class MainWindow(QtWidgets.QDialog):
DEFAULT_SIZE = (300, 325)
def __init__(self):
super().__init__()
self.resize(*MainWindow.DEFAULT_SIZE)
self.setWindowTitle("Todo List")
main_layout = QtWidgets.QVBoxLayout(self)
self.todo_wgt = QtWidgets.QTreeWidget()
self.todo_wgt.setHeaderLabel("Tasks")
top_task = QtWidgets.QTreeWidgetItem(["Quadratics Homework"])
self.todo_wgt.addTopLevelItem(top_task)
sub_task = QtWidgets.QTreeWidgetItem(["Complete the square"])
top_task.addChild(sub_task)
main_layout.addWidget(self.todo_wgt)
done
looks good now
See, not so bad?
or better atleast
i think the whole view thing was confusing me
yeah, stay away from views for now
i like how on VSC i can hover over methods or whatever and it tells me the params
even still I rarely use them, although I do know their benefits
its helpful knowing what i need to pass in
yeah that's super helpful. I still use it all the time
Imagine we had a list of data (like a python list), and every time it changed, a widget would also update based on that
that's more similar to what a view is
also, i think i want a different method of adding tasks this time
instead of a line edit?
yeah i think it would be a nice difference
What did you have in mind?
you can right click to bring up a context menu
and you can add tasks, delete tasks, edit tasks
ahh ok, perfect
and add subtasks if you rightclick over an existing task
i just think it could look cleaner
I agree
Most widgets actually have right-click menus built in
but they're disabled by default
oh awesome
we just need to re-enable it
okay lets start with adding main tasks to the QTreeWidget?
mind sending what gui so far looks like as an update ? 
are u following every change he makes/sends locally ? 
βcomplete the squareβ π
not exactly
I have todo_wdg, he has todo_wgt
π
lol
Well let's add the right click menu, and from there we can add our options and methods
So get ready for a mouthful
self.todo_wdg.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
it's one line, but it's long
we're setting the ContextMenuPolicy of the widget
and we're setting it to CustomContextMenu
well, there's 2 ways we can do this
i feel like longest qt line u have written is like at least double if not triple this 
oh yeah for sure
why the hell is their a policy
policy is basically a fancy word for "setting"
oh lol
there's different "settings" we could choose for how right-click works on our widget
so its setting the context menu to a custom one
by default it's NoContextMenu
for example though, look if we use a QLineEdit
it has a DefaultContextMenu
pretty common in text entry widgets
correct
cool
we've just told it "I want to show a menu when I right click"
i assume it needs a signal
but we need to create a slot for it
exactly π
okay let me try this
this one is a bit tricky
oh yeah, that's it
im curious if it was at all avoidable
that's usually why I refactor them into methods I never have to look at again
self.todo_wdg.customContextMenuRequested.connect(self.on_right_click)
I'm connecting to a new method on_right_click
welp thats a darn good tactic
def on_right_click(self):
print("Hurray!")
yep i made my method
I usually do something like this and test it to make sure it's already working as expected
okay my console is succesfully printing yippee on right click
perfect
so my next step issss to add something to the context menu
ok, so we actually need to create the menu
we want to create the menu first, and then display the menu on right click
if we wanted it a bit more dynamic, we could rebuild it each time on right click
like if clicking subtasks was different from clicking top tasks
yeah ok, good
Let's actually create another method for creating our menu
I usually organize my class by widgets, layouts, connections, and menus
||is there a signal that needs to be connected for hiding the menu as well so it isnt there 24/7?||
popup widgets know to disappear when clicking away
done
we can technically designate any widget to be a popup widget with a flag
which flag 
i think i do that
Qt.Popup, haha
class MainWindow(QtWidgets.QDialog):
DEFAULT_SIZE = (300, 325)
def __init__(self):
super().__init__()
self.resize(*MainWindow.DEFAULT_SIZE)
self.setWindowTitle("Todo List")
main_layout = QtWidgets.QVBoxLayout(self)
self.todo_wgt = QtWidgets.QTreeWidget()
self.todo_wgt.setHeaderLabel("Tasks")
self.todo_wgt.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
main_layout.addWidget(self.todo_wgt)
self.todo_wgt.customContextMenuRequested.connect(self.on_right_click)
def on_right_click(self):
print("yippee")
def create_menu(self):
class PopupTool(QtWidgets.QDialog):
def __init__(self):
super().__init__(parent=main_maya_window())
self.setWindowFlags(Qt.Popup)
I just use this and inherit from it whenever I want a custom popup tool
its literally called Qt.Popup got it 
ok, so we'll create QMenu, and that's what will hold all of our actions
alright
self.right_click_menu = QtWidgets.QMenu(self)
we're also giving it self as parent so it knows who the menu belongs to
if we plan on attaching this to something like a menu bar, it's less important
ok so for each action, we do this in 2 steps. First we add the action, then we connects its signal
okay
add_task_action = QtGui.QAction("New Task...")
add_task_action.triggered.connect(self.on_add_task_triggered)
We'll add it to the menu in a moment
all in the create menu method right?
Yes
cool
yep thats fine
that makes it a bit easier if I want to adjust the order they appear
Sure, you can
okay done that
Ok, make sure you make the methods as well, even if they just have pass for now
yes i have
perfect
there's one thing I realized we're missing
add_task_action = QtGui.QAction("New Task...", self)
we need self as an argument for the action
Well, either we need this, or we need add_task_action to be an attribute
self.add_task_action
as you might know, objects without references to them are destroyed
and local variables no longer exist after a function is called
so we need some way to preserve the actions otherwise they get removed
technically we could create this "add task action" and add it to many different menus. Actions are really useful that way
anyways, just add self like I have above and we can move on π
Inside the on_right_click method, now we just need to show the menu
Oh, and make sure you're calling the create_menu method from __init__
what do you mean?
we made a method for creating the menu, but the menu won't be created unless we call that method
def __init__(self):
super().__init__()
main_layout = QtWidgets.QVBoxLayout(self)
self.todo_wdg = QtWidgets.QTreeWidget()
self.todo_wdg.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.todo_wdg.customContextMenuRequested.connect(self.on_right_click)
self.todo_wdg.setHeaderLabel('Tasks')
main_layout.addWidget(self.todo_wdg)
top_task = QtWidgets.QTreeWidgetItem(["Quadratics Homework"])
self.todo_wdg.addTopLevelItem(top_task)
child_task = QtWidgets.QTreeWidgetItem(["10 Exercises"])
top_task.addChild(child_task)
self.create_menu()
this is my __init__ right now
oh okay
to display our menu, it's simply self.right_click_menu.exec()
but, we need an argument
well, try it first without
yes, that's where we're doing exec on our menu
nothing is popping up
||on a bold assumption here but, is argument parent ? ||
Good guess, but nope
My last message is a hint
I meant the top left of your entire monitor, not just the GUI window
it looks the same
show your latest code?
from PySide6 import QtWidgets, QtGui, QtCore
from PySide6.QtCore import Qt
class MainWindow(QtWidgets.QDialog):
DEFAULT_SIZE = (300, 325)
def __init__(self):
super().__init__()
self.resize(*MainWindow.DEFAULT_SIZE)
self.setWindowTitle("Todo List")
main_layout = QtWidgets.QVBoxLayout(self)
self.todo_wgt = QtWidgets.QTreeWidget()
self.todo_wgt.setHeaderLabel("Tasks")
self.todo_wgt.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
main_layout.addWidget(self.todo_wgt)
self.create_menu()
self.todo_wgt.customContextMenuRequested.connect(self.on_right_click)
def on_right_click(self):
self.right_click_menu.exec()
def create_menu(self):
self.right_click_menu = QtWidgets.QMenu(self)
add_task_action = QtGui.QAction("New Task...", self)
add_task_action.triggered.connect(self.on_add_task_triggered)
edit_task_action = QtGui.QAction("Edit Task", self)
edit_task_action.triggered.connect(self.on_edit_task_triggered)
delete_task_action = QtGui.QAction("Delete Task", self)
delete_task_action.triggered.connect(self.on_delete_task_triggered)
def on_add_task_triggered(self):
pass
def on_edit_task_triggered(self):
pass
def on_delete_task_triggered(self):
pass
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec()
ahh right we never added our actions to the menu
ohh lol
self.right_click_menu.addAction(add_task_action)
I would do this below where you create all your actions though
they will appear in the order you have them added
also slight side note but DEFAULT_SIZE = (300, 325) and DEFAULT_SIZE = 300, 325 work exactly the same if u didnt know 
okay will do
Here's a recent example from my code
you can see how I have them at the bottom, which makes it easy to rearrange
Yeah, I always have a method for creating widgets and all widgets go there
and same for layouts and connections
how would i do that
so your init basically just looks like
exactly how you have it now, but tucked inside methods instead
here's a recent example from mine
class AssetSelectWindow(ui_utils.MayaWindow):
PACKAGE_ROLE = Qt.UserRole
icon_size = QtCore.QSize(100, 100)
icon_scale = 1.2
def __init__(self):
super().__init__()
self.setWindowTitle("Select your fighter!")
self.create_widgets()
self.create_layouts()
self.create_connections()
if os.path.exists(RIG_MANIFEST):
rig_packages = get_packages_from_manifest()
else:
rig_packages = get_all_rig_packages()
self.add_packages_to_css(rig_packages)
self.resize(750, 550)
okay ill try
__init__ is nice and organized. Everything exciting is going on in those 3 other methods
okay
def create_connections(self):
self.search_bar.textChanged.connect(self.proxy_model.setFilterRegExp)
self.toggle_grid_mode_btn.toggled.connect(self.toggle_grid_mode)
self.refresh_rig_list_btn.clicked.connect(self.reload_rig_manifest)
self.missing_ref_btn.clicked.connect(self.on_missing_ref_btn_clicked)
self.repair_textures_btn.clicked.connect(self.on_repair_textures_btn_clicked)
im curious if u had qt widget classes with like 20+ methods
I'm not sure what you mean?
this one has like 6 methods, did u have one with 20+ ? 
from PySide6 import QtWidgets, QtGui, QtCore
from PySide6.QtCore import Qt
class MainWindow(QtWidgets.QDialog):
DEFAULT_SIZE = (300, 325)
def __init__(self):
super().__init__()
self.resize(*MainWindow.DEFAULT_SIZE)
self.setWindowTitle("Todo List")
self.create_widgets()
self.create_layout()
self.create_connections()
self.create_menu()
def create_widgets(self):
self.todo_wgt = QtWidgets.QTreeWidget()
self.todo_wgt.setHeaderLabel("Tasks")
self.todo_wgt.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
def create_layout(self):
main_layout = QtWidgets.QVBoxLayout(self)
main_layout.addWidget(self.todo_wgt)
def create_connections(self):
self.todo_wgt.customContextMenuRequested.connect(self.on_right_click)
def create_menu(self):
self.right_click_menu = QtWidgets.QMenu(self)
add_task_action = QtGui.QAction("New Task...", self)
add_task_action.triggered.connect(self.on_add_task_triggered)
edit_task_action = QtGui.QAction("Edit Task", self)
edit_task_action.triggered.connect(self.on_edit_task_triggered)
delete_task_action = QtGui.QAction("Delete Task", self)
delete_task_action.triggered.connect(self.on_delete_task_triggered)
self.right_click_menu.addAction(add_task_action)
self.right_click_menu.addAction(edit_task_action)
self.right_click_menu.addAction(delete_task_action)
def on_right_click(self):
self.right_click_menu.exec()
def on_add_task_triggered(self):
pass
def on_edit_task_triggered(self):
pass
def on_delete_task_triggered(self):
pass
app = QtWidgets.QApplication()
window = MainWindow()
window.show()
app.exec()
thats my entire code now
Hard to say exactly because stuff is really nested. One widget might have 5 connections but it might also contain a widget that has 5 connections of its own
like one UI might be 7 different classes
yup, perfect
awesome
ok, so for exec, we need to popup the menu at our mouse cursor
we can get our mouse position quite easily in pyside
QtGui.QCursor().pos()
okay
So what should happen when you click New Task?
You can make the items in your tree editable
hold on where do i put this
or you can make a popup window you can type into
inside exec()
okay
well
you should be prompted to enter a task name
then when enter is pressed it adds it
Ok, so like a little dialog box?
Yes like that
is there a bible out there for all these naming conventions in gui
Yes looks good
I'm not quite sure
no matter how many times i think "surely there cant be another term im not familiar with in gui" i find like 3
then i find another 3 i already knew meant different things
I would just have a look through all the QtWidget class names and click anything that sounds unfamiliar
that's how I learned about a lot of new widgets
fortunately pyside has a built in class for quick simple dialog windows like this
oh awesome lets do it
well it depends what you right clicked on
ok, so we need to know what we have selected when the right click menu appears
yes
I would recommend adding your example Quadratic Homework task back in
just to make it easier to test things
okay let me try again
I almost always have sample data in my GUI when I'm working on it
whys it only displaying the first character of the tasks label
Remember your QTreeWidgetItem must be a list (or rather, any iterable)
if you give it a string, it treats each character of the string as a different column
oh yeah
I make that mistake all the time too
okay sorted
so, now we're going to work inside the on_add_task_triggered method
because that's where we need to decide what will happen when that action is selected from the menu
Let's first figure out what we have selected
yes
edit and delete shouldnt matter whats selected
oh i guess it matters whats selected
i just assumed that right clicking over whatever it is, is the one targetted
No, we have to get our selection once the action item is triggered
Unless we want it so right clicking can't modify selection
like you make your selection and then right click
this is UX design in a nutshell
you have to work out the user experience
how exactly do they interact with your UI
try printing it out so you can see what it is
i get a memory location of an object
it's a list of QTreeWidgetItems
even though we can only select 1 thing at a time, it's still a list
try launching it again and right click > add task before selecting anything
empty list
exactly
so, we need to decide the scenario that leads to adding a top task, and what scenario leads to adding a child task
I think "no selection" should obviously be "new top task"
yes this makes sense
so if selected == []
make new top task
we can just say if not selected
oh does [] mean theres nothing there
i thought it would still mean selected is something
yes it's the same thing, but the "pythonic" way would be if not selected
okay i see
should i put this between my add and edit selected methods
it doesn't really matter
you can organize it if you want
I don't really organize my methods too much
so you pretty much already have the logic for this
lol okay
since you made your example tasks
I'll brb in a few minutes, see if you can figure it out
yes let me try
Remember it's just
Make new qtreewidgetitem
Add qtreewidgetitem to the qtreewidget
i did that
now i need the dialog
def add_new_top_task(self, item):
self.new_top_task = QtWidgets.QTreeWidgetItem([item.text()])
self.todo_wgt.addTopLevelItem(self.new_top_task)
I don't know where you got item.text() from
we simply want to give it the string of the new task's name
i used it in my last todo list
def add_new_top_task(self, task_name):
top_task = QtWidgets.QTreeWidgetItem([task_name])
we just want to pass the parameter directly into it
okay
the reason we're making this its own method is imagine we're reading from a text file instead
we want a nice method to be able to create our items
we don't care where the text is coming from
we just want to say "add a new task with this name"
i mean i probably need a text file or something at some point to save tasks over uses
probably json
this is also where we can put any item styling
so for example, we can add our checkbox from here
oh yes
This is an example of where we need to give the column name to the item
top_task.setCheckState(0, Qt.CheckState.Unchecked)
so it's nearly the same as list widget, but we have to tell it column 0 should have a checkbox
inside where do i put this?
still inside the add_new_top_task method
okay
def add_new_top_task(self, task_name):
top_task = QtWidgets.QTreeWidgetItem([task_name])
top_task.setCheckState(0, Qt.CheckState.Unchecked)
self.todo_wgt.addTopLevelItem(top_task)
return top_task
This is what I have. We want to make sure to return the new item as well
that way we can access it later on after adding it
okay cool ive done that
Ok back to the "on_add_task_triggered" method
alright
we can start to test this by calling the method
we need to call the method
def on_add_task_triggered(self):
selected = self.todo_wgt.selectedItems()
if not selected:
self.add_new_top_task("Example")
infact i did it when i made the method
This is a good way to test that your methods are already hooked up correctly
the first task wasn't added using the new method we just created, so we can update that too
yesss
but we can wait until we have the subtask method too
it stops working once you select a task
Do you want subtasks to be able to have subtasks?
but thats fine
Yeah, that's expected for now
i mean it could be infinite
i dont want limits
ok good, so we don't even have to limit it
i actually wanna use this program potentially so
i wanna tailor it perfectly how i want it
So there's a pretty simple way to work out if something is a child or not
we can get selected[0].parent()
remember selected is a list
if it's a top level item, parent() will be None
otherwise, it will be another QTreeWidgetItem
im confused why it gave me a memory location earlier
:white_check_mark: Your 3.13 eval job has completed with return code 0.
<__main__.Foo object at 0x7fdf6bb86a50>
oh okay
What did you expect it to look like?
<PySide6.QtWidgets.QTreeWidgetItem object at 0x000001C89FC92140>
get used to seeing instances like this
all we need to know is that it's a QTreeWidgetItem
okay
one sec I'm going to reset my PC real quick, it's acting up
we need a method for adding a sub item though
we basically have the logic for that too written above when you add your example task
see if you can work it out while I reset
okay will do
Pc is updating π¬
but how do i use the param to add the subtask too it
windows i imagine
yeah, finally updated
you're already doing that to add your subtask to quadratics homework
ef add_new_sub_task(self, task_name, parent):
new_sub_task = QtWidgets.QTreeWidgetItem([task_name])
new_sub_task.setCheckState(0, Qt.CheckState.Unchecked)
no because
i cant do
self.parent.addChild
so what do i do
you just do parent.addChild
that's the parameter
I wouldn't even call the parameter parent
just call it item
item.addChild(new_sub_task)
so how do i pass in parent
we'll work that out now
the method doesn't care though
we want to keep methods as simple and generic as possible
it simply says "give me an item, and I will create a new item and make it a child of that item"
again, we don't care how it knows where the item is coming from
it could be from a click
it could be randomly selected
it could be "2 items down"
that's only for us to worry about when we call the method, not when we write the method
okay
What does your sub item method look like now?
def add_new_sub_task(self, task_name, parent):
new_sub_task = QtWidgets.QTreeWidgetItem([task_name])
new_sub_task.setCheckState(0, Qt.CheckState.Unchecked)
parent.addChild(new_sub_task)
return new_sub_task
perfect
Ok so when we right click and add task
it's a top level task if we have no selection, or if our item has no parent
wait hang on
if we're selecting a top level item, we probably want our new task to be a child of that item
yes
how do you want to add new top level tasks then?
it works right now no?
only when you first launch and have no selection
but once you select something, you can't deselect
ok we can change this just a bit
instead of selectedItems
we can use itemAt
to get the widget at our cursor
okay done
just a sec, I need to work this out, haha
its okay lol
still learning about qtree as well π
its fine lol i still wanna cry reading a thousand different self. attributes
lmao :-;
I was getting the position of the mouse when I clicked the menu, not when I clicked the item
also i just wanna say im very appreciative of the help youve done
wouldnt of learnt this without you
my brains gonna explode lol
Happy to help! I learn a lot from it as well
There's a few different ways to go about solving this and I'm trying to think of what would be the least painful
ah damn it man
I think our best bet is to store the mouse position when we bring up the right click menu
Your homework is to open a blank file and rebuild this again
but ive definitely learnt alot today
now i am become programmer, creater of GUI
This help channel has been closed. 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.