So I got to change the colors as well as change colors while hovering over menu objects created using the OptionMenu in tkinter.
I even have text printing when I hover over the button, but the second I drop down the menu, it won't print anymore.
What am I doing wrong? How can I print when you click to open the OptionMenu and and move around through the selections?
from tkinter import *
import tkinter as tk
OZoneIzotopeSemiWhite = "#c0c4ca"
buttonBackground = "#303336"
buttonforeground = "#cdd0d7"
BACKGROUND2 = "#1e1f21"
class DropDownButton():
def __init__(self, parent, placement, opTions, **kw):
self.parent = parent
self.options = opTions
self.om_variable = tk.StringVar(self.parent)
self.om_variable.set(self.options[0])
self.om_variable.trace('w', self.option_select)
self.om = tk.OptionMenu(self.parent, self.om_variable, *self.options)
self.om["menu"].config(fg=buttonforeground, bg=buttonBackground, activebackground=OZoneIzotopeSemiWhite, activeforeground=BACKGROUND2, borderwidth = 0)
self.om.config(fg=buttonforeground, bg=buttonBackground, activebackground=OZoneIzotopeSemiWhite, activeforeground=BACKGROUND2, bd =0)
self.om.place(x = placement, y = 2)
self.om.bind("<Enter>", self.on_enter)
self.om.bind("<Leave>", self.on_leave)
def on_enter(self, event):
if self.om == self.options[0]:
print ("Hello")
elif self.om_variable.get() == self.options[1]:
print ("Hello 2!")
else:
print("Hell0 3!")
def on_leave(self, enter):
print ("leave")
def option_select(self, *args):
print (self.om_variable.get())
root = tk.Tk()
DropDownButton(root, 55, ['one', 'two', 'three'])
root.mainloop()
You're not doing anything wrong as such, but you might need to alter what you do or your expectations. When a menu is popped up by clicking on a menubutton widget — as created by the option menu code — the mouse pointer is grabbed by the menu that has popped up, and this state continues until you do an action that selects something (or you click or release outside the menu, which cancels). Your styling, which depends on the state of the menubutton, might be noticing this and going into something slightly unexpected to you.
By the way, if you pop up the menu using key bindings, the differing look is actually useful as the focus will also be on the actual popup menu.
Related
This is an extension to the question and answers here.
I want to create two Comboboxes, with the items in the second Combobox depending on the selection in the first Combobox. Furthermore, I would like the dropdown listbox to resize to fit the text in the list, as in the answer here. However, I'm having some difficulties with this second part. I'd like to solve this problem using Python.
I've had varying results using .pack() and .forget() instead of .place() and .place_forget(), however I haven't been able to create a robust solution. Using .place is preferable to .pack or .grid if possible.
As an MWE, I've extended the code from one of the answers in the previous question. The dropdown listbox of c2 resizes fine, however that of c1 does not.
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
def on_combo_configure(event):
combo = event.widget
style = ttk.Style()
# check if the combobox already has the "postoffest" property
current_combo_style = combo.cget('style') or "TCombobox"
if len(style.lookup(current_combo_style, 'postoffset'))>0:
return
combo_values = combo.cget('values')
if len(combo_values) == 0:
return
longest_value = max(combo_values, key=len)
font = tkfont.nametofont(str(combo.cget('font')))
width = font.measure(longest_value + "0") - event.width
if (width<0):
# no need to make the popdown smaller
return
# create an unique style name using widget's id
unique_name='Combobox{}'.format(combo.winfo_id())
# the new style must inherit from curret widget style (unless it's our custom style!)
if unique_name in current_combo_style:
style_name = current_combo_style
else:
style_name = "{}.{}".format(unique_name, current_combo_style)
style.configure(style_name, postoffset=(0,0,width,0))
combo.configure(style=style_name)
def update_c1_list(event):
c1.place_forget()
_ = c.get()
if _ == "fruit":
c1['values'] = ('apples are the best', 'bananas are way more better')
elif _ == "text":
c1['values'] = ("here's some text", "and here's some much longer text to stretch the list")
else:
pass
c1.place(x=10,y=40)
root = tk.Tk()
root.title("testing the combobox")
root.geometry('300x300+50+50')
c = ttk.Combobox(root, values=['fruit','text'], state="readonly", width=10)
c.bind('<Configure>', on_combo_configure)
c.bind('<<ComboboxSelected>>', update_c1_list)
c.place(x=10,y=10)
c1 = ttk.Combobox(root, state="readonly", width=10)
c1.bind('<Configure>', on_combo_configure)
c1.place(x=10,y=40)
c2 = ttk.Combobox(root, state="readonly", width=10)
c2.bind('<Configure>', on_combo_configure)
c2.place(x=10,y=70)
c2['values']=('this list resizes fine','because it is updated outside of the function')
root.mainloop()
Any help is welcome, thanks.
After playing around I came up with a function which updates the listbox width of a combobox "on the fly". However, it's a bit like fixing a window with a hammer and causes some issues.
def Change_combo_width_on_the_fly(combo,combo_width):
style = ttk.Style()
# check if the combobox already has the "postoffest" property
current_combo_style = combo.cget('style') or "TCombobox"
combo_values = combo.cget('values')
if len(combo_values) == 0:
return
longest_value = max(combo_values, key=len)
font = tkfont.nametofont(str(combo.cget('font')))
width = font.measure(longest_value + "0") - (combo_width*6+23)
if (width<0):
# no need to make the popdown smaller
return
# create an unique style name using widget's id
unique_name='Combobox{}'.format(combo.winfo_id())
# the new style must inherit from curret widget style (unless it's our custom style!)
if unique_name in current_combo_style:
style_name = current_combo_style
else:
style_name = "{}.{}".format(unique_name, current_combo_style)
style.configure(style_name, postoffset=(0,0,width,0))
combo.configure(style=style_name)
As a MWE, the code can be used as follows:
import tkinter as tk
import tkinter.ttk as ttk
import tkinter.font as tkfont
root = tk.Tk()
c1 = ttk.Combobox(root, state="readonly", width=10)
c1.place(x=10,y=40)
Change_combo_width_on_the_fly(c1,10)
root.mainloop()
While the function does the job, it causes problems elsewhere in my code. In particular, it messes with a previously packed widget (scrollbar). I think it is changing the style the last placed widget, but I don't know how to fix this.
So, I was recently checking out VsCode, and I noticed an interesting feature. Although there was a taskbar icon, there was no titlebar; instead, VsCode implements its own. I looked at some other programs from Microsoft, and they do the same thing. I think this is a very cool feature.
I make a lot of productivity apps with Tkinter*, so I looked at how to do this in my apps. Unfortunately, the standard way to get rid of the titlebar in Tkinter is to disable the Window Manager (using overridedirect(1)). This also gets rid of the taskbar icon, which I want to keep.
In other words, what I am trying to get is
while still keeping this: .
* For reference I am using Python 3.8 and TkInter 8.6.
You can create your own buttons titlebar using frames. Here take a look at this. I also worked on a tkinter based app and created this along with using what #Hruthik Reddy has given.
I make a lot of productivity apps with Tkinter*, so I looked at how to do this in my apps.
Assuming that you may have used classes at some point in those apps, I have created this inherited Tk subclass, and explained in comments:
import tkinter as tk
import tkinter.ttk as ttk
from ctypes import windll
class TestApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
# set overrideredirect to True to remove the windows default decorators
self.overrideredirect(True)
self.geometry('700x500+10+10') # you may or may not want to initialize the geometry of the window
self.minsize(193, 109)
# (x, y) coordinates from top left corner of the window
self.x = None
self.y = None
# Create a frame that will contain the title label of the window
self.frame = tk.Frame(self, bg='gray38')
self.frame.pack(side=tk.TOP, fill=tk.X)
# Label `name` for the window
# Since buttons are on the right side and the name of the window is on the left side, the label will be packed towards LEFT side
self.name = tk.Label(self.frame, text='Simple Text Box', font='Consolas 11',
bg=self.frame.cget('background'), fg='white')
self.name.pack(side=tk.LEFT, fill=tk.X, anchor=tk.CENTER)
# Pack the close button to the right-most side
self.close = tk.Button(self.frame, text='✕', bd=0, width=3, font='Consolas 13',
command=self.destroy, bg=self.frame.cget('background'))
self.close.pack(side=tk.RIGHT)
# Pack the maximize button second from the right
# The unicode string as the value of the keyword `text` below, is taken from the internet, it contains the maximize icon as unicode character
self.maximize = tk.Button(self.frame, text=u"\U0001F5D6", bd=0, width=3, font='Consolas',
command=self.maximize_win, bg=self.frame.cget('background'))
self.maximize.pack(side=tk.RIGHT)
# Pack the minimize button third from the right
self.minimize = tk.Button(self.frame, text='—', bd=0, width=3, font='Consolas 13',
command=self.minimize_win, bg=self.frame.cget('background'))
self.minimize.pack(side=tk.RIGHT)
# -------------------
# NOW YOU CAN PUT WHATEVER WIDGETS YOU WANT AFTER THIS BUT FOR THIS EXAMPLE I
# HAVE TAKEN A TEXTBOX WITH HORIZONTAL AND VERTICAL SCROLLBARS AND A SIZEGRIP
# -------------------
# The frame below contains the vertical scrollbar and the sizegrip (sizegrip helps in resizing the window
self.scroll_frame = tk.Frame(self)
v_scroll = tk.Scrollbar(self.scroll_frame, orient=tk.VERTICAL)
h_scroll = tk.Scrollbar(self, orient=tk.HORIZONTAL)
self.grip = ttk.Sizegrip(self.scroll_frame)
# I am directly putting the textbox in the window, you may add frames and other stuff
self.text = tk.Text(self, wrap=tk.NONE, yscrollcommand=v_scroll.set, xscrollcommand=h_scroll.set,
font='Consolas 14', width=1, height=1)
# set the scrollbar for y and x views of the textbox respectively
v_scroll.config(command=self.text.yview)
h_scroll.config(command=self.text.xview)
# Packing scrollbar frame, the scrollbars and the grip according to the arrangement I want
self.scroll_frame.pack(side=tk.RIGHT, fill=tk.Y)
v_scroll.pack(side=tk.TOP, fill=tk.Y, expand=tk.Y)
self.grip.pack(side=tk.BOTTOM)
self.text.pack(side=tk.TOP, expand=tk.TRUE, fill=tk.BOTH)
h_scroll.pack(side=tk.BOTTOM, fill=tk.X)
self.grip.bind("<B1-Motion>", self.onmotion)
# Bind the motion of mouse after mouse click to the onmotion function for window resizing
self.call('encoding', 'system', 'utf-8')
# Binding `<Enter>` and `<Leave>` mouse event to their respective functions
# `<Enter>` event is called when the mouse pointer enters any widget
# `<Leave>` event is called when the mouse pointer leaves any widget
# Here when the mouse pointer enters or leaves the buttons their color will change
self.close.bind('<Enter>', lambda _: self.close.config(bg='red'))
self.close.bind('<Leave>', lambda _: self.close.config(bg=self.frame.cget('background')))
self.minimize.bind('<Enter>', lambda _: self.minimize.config(bg='gray58'))
self.minimize.bind('<Leave>', lambda _: self.minimize.config(bg=self.frame.cget('background')))
self.maximize.bind('<Enter>', lambda _: self.maximize.config(bg='gray58'))
self.maximize.bind('<Leave>', lambda _: self.maximize.config(bg=self.frame.cget('background')))
# Now you may want to move your window (obviously), so the respective events are bound to the functions
self.frame.bind("<ButtonPress-1>", self.start_move)
self.frame.bind("<ButtonRelease-1>", self.stop_move)
self.frame.bind("<B1-Motion>", self.do_move)
self.frame.bind('<Double-1>', self.maximize_win)
self.name.bind("<ButtonPress-1>", self.start_move)
self.name.bind("<ButtonRelease-1>", self.stop_move)
self.name.bind("<B1-Motion>", self.do_move)
self.name.bind('<Double-1>', self.maximize_win)
def start_move(self, event):
""" change the (x, y) coordinate on mousebutton press and hold motion """
self.x = event.x
self.y = event.y
def stop_move(self, event):
""" when mouse button is released set the (x, y) coordinates to None """
self.x = None
self.y = None
def do_move(self, event):
""" function to move the window """
self.wm_state('normal') # if window is maximized, set it to normal (or resizable)
self.maximize.config(text=u"\U0001F5D6") # set the maximize button text to the square character of maximizing window
deltax = event.x - self.x
deltay = event.y - self.y
x = self.winfo_x() + deltax
y = self.winfo_y() + deltay
self.geometry(f"+{x}+{y}")
def onmotion(self, event):
""" function to change window size """
self.wm_state('normal')
self.maximize.config(text=u"\U0001F5D6")
x1 = self.winfo_pointerx()
y1 = self.winfo_pointery()
x0 = self.winfo_rootx()
y0 = self.winfo_rooty()
self.geometry("%sx%s" % ((x1-x0), (y1-y0)))
return
def minimize_win(self, event=None):
""" function to iconify or minimize window as an icon """
self.overrideredirect(False)
self.wm_iconify()
self.bind('<FocusIn>', self.on_deiconify)
def maximize_win(self, event=None):
""" function to maximize window or make it normal (exit maximize) """
if self.maximize.cget('text') == u"\U0001F5D7":
self.wm_state('normal')
self.maximize.config(text=u"\U0001F5D6")
return
self.wm_state('zoomed')
self.maximize.config(text=u"\U0001F5D7")
def on_deiconify(self, event):
""" function to deiconify or window """
self.overrideredirect(True)
set_appwindow(root=self)
def set_appwindow(root):
hwnd = windll.user32.GetParent(root.winfo_id())
style = windll.user32.GetWindowLongPtrW(hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_TOOLWINDOW
style = style | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongPtrW(hwnd, GWL_EXSTYLE, style)
# re-assert the new window style
root.wm_withdraw()
root.after(10, lambda: root.wm_deiconify())
if __name__ == '__main__':
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080
app = TestApp()
# print(app.tk.call('tk', 'windowingsystem'))
# # Here root.tk.call('tk', 'windowingsystem') calls tk windowingsystem in Tcl, and that returns 'win32',
# # 'aqua' or 'x11' as documented in tk
app.after(10, lambda: set_appwindow(root=app))
app.text.insert(1.0, 'Drag the window using the title or the empty area to the right of the\ntitle.'
' Try maximizing / minimizing.\n\n-- YOU MAY HAVE A PROBLEM WITH RESIZING --\n'
'-- ALSO IF YOU REMOVE `height` AND `width` KEYWORDS FROM THE TEXTBOX DECLARATION'
' AND FONT SIZE IS TOO BIG THE SCROLLBAR MAY DISAPPEAR --\nSO KEEP THOSE KEYWORDS THERE!')
app.mainloop()
When window is maximized you may not be able to see the taskbar. But you can still resize it using sizegrip. I don't yet know how to make window resizing possible from window borders with overrideredirect but Sizegrip works just fine.
Now regarding the set_appwindow function, this is what MSDN says:
The Shell creates a button on the taskbar whenever an application
creates a window that isn't owned. To ensure that the window button is
placed on the taskbar, create an unowned window with the
WS_EX_APPWINDOW extended style. To prevent the window button from
being placed on the taskbar, create the unowned window with the
WS_EX_TOOLWINDOW extended style. As an alternative, you can create a
hidden window and make this hidden window the owner of your visible
window.
Complete reference here
This may seem like a very long answer but I hope it covers all what you need and helps you.
Check out the following code it worked for me :-
import tkinter as tk
import tkinter.ttk as ttk
from ctypes import windll
GWL_EXSTYLE=-20
WS_EX_APPWINDOW=0x00040000
WS_EX_TOOLWINDOW=0x00000080
def set_appwindow(root):
hwnd = windll.user32.GetParent(root.winfo_id())
style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_TOOLWINDOW
style = style | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style)
root.wm_withdraw()
root.after(10, lambda: root.wm_deiconify())
def main():
root = tk.Tk()
root.wm_title("AppWindow Test")
button = ttk.Button(root, text='Exit', command=lambda: root.destroy())
button.place(x=10,y=10)
root.overrideredirect(True)
root.after(10, lambda: set_appwindow(root))
root.mainloop()
if __name__ == '__main__':
main()
In the function on_deiconify() we need to unbind the previous event <FocusIn> to avoid from blitting of the window we write like this
def on_deiconify(self, event):
""" function to deiconify or window """
self.overrideredirect(True)
set_appwindow(root=self)
self.unbind("<FocusIn>")
I'm creating a simple user dialog window with a basic text on top and a tree view with one column below, that gives the user a couple of choices. A button at the bottom is used to confirm the selection.
Now I can't get the Message widget, which I use to display the instructions, to fill the Frame I've created for it. Meanwhile, the Treeview widget fills the Frame as I want it to.
Many proposed solutions on other StackOverflow questions state, that putting my_message.pack(fill=tk.X, expand=True) should work. It doesn't in my case.. In a different scenario it is recommended to put my_frame.columnconfigure(0, weight=1), which doesn't help either.
Here is the code:
import tkinter as tk
from tkinter import ttk
class MessageBox(object):
""" Adjusted code from StackOverflow #10057662. """
def __init__(self, msg, option_list):
root = self.root = tk.Tk()
root.geometry("400x400")
root.title('Message')
self.msg = str(msg)
frm_1 = tk.Frame(root)
frm_1.pack(expand=True, fill=tk.X, ipadx=2, ipady=2)
message = tk.Message(frm_1, text=self.msg)
message.pack(expand=True, fill=tk.X) # <------------------------------------ This doesn't show the desired effect!
frm_1.columnconfigure(0, weight=1)
self.tree_view = ttk.Treeview(frm_1)
self.tree_view.heading("#0", text="Filename", anchor=tk.CENTER)
for idx, option in enumerate(option_list):
self.tree_view.insert("", idx+1, text=option)
self.tree_view.pack(fill=tk.X, padx=2, pady=2)
choice_msg = "Long Test string to show, that my frame is unfortunately not correctly filled from side to side, as I would want it to."
choices = ["Test 1", "Test 2", "Test 3"]
test = MessageBox(choice_msg, choices)
test.root.mainloop()
I'm slowly going nuts, because I know that there is probably something very basic overruling the correct positioning of the widget, but I've been trying different StackOverflow solutions and browsing documentation for hours now with no luck.
Try to set a width of message in the tk.Message constructor, something like this:
message = tk.Message(frm_1, text=self.msg, width=400-10) # 400 - is your window width
message.pack() # In that case you can delete <expand=True, fill=tk.X>
The problem you are facing is a feature: The Message widget tries to lay out the text in one of two ways:
according to an aspect (width-to-height ratio in percent)
according to a maximum width (lines will be broken if longer)
Both of these goals seem not to cooperate well with the automatic resizing a Message widget experiences from a grid or pack layout manager. What can be done is to bind a handler to the resize event of the widget to adjusts the width option dynamically. Also, there are better options to use with the pack layout manager than shown in OP.
I derived an AutoMessage widget to get the event handler out of the way:
import tkinter as tk
from tkinter import ttk
class AutoMessage(tk.Message):
"""Message that adapts its width option to its actual window width"""
def __init__(self, parent, *args, **options):
tk.Message.__init__(self, parent, *args, **options)
# The value 4 was found by experiment, it prevents text to be
# displayed outside of the widget (exceeding the right border)
self.padx = 4 + 2 * options.get("padx", 0)
self.bind("<Configure>", self.resize_handler)
def resize_handler(self, event):
self.configure(width=event.width - self.padx)
class MessageBox(object):
"""Adjusted code from StackOverflow #10057662."""
def __init__(self, msg, option_list):
root = self.root = tk.Tk()
root.geometry("400x400")
root.title("Message")
self.msg = str(msg)
self.frm_1 = tk.Frame(root)
self.frm_1.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
self.message = AutoMessage(self.frm_1, text=self.msg, anchor=tk.W)
self.message.pack(side=tk.TOP, fill=tk.X)
self.frm_1.columnconfigure(0, weight=1)
self.tree_view = ttk.Treeview(self.frm_1)
self.tree_view.heading("#0", text="Filename", anchor=tk.CENTER)
for idx, option in enumerate(option_list):
self.tree_view.insert("", idx + 1, text=option)
self.tree_view.pack(fill=tk.X, padx=2, pady=2)
choice_msg = "Long Test string to show, that my frame is unfortunately not correctly filled from side to side, as I would want it to."
choices = ["Test 1", "Test 2", "Test 3"]
test = MessageBox(choice_msg, choices)
test.root.mainloop()
As someone new to tkinter for Python 3, I am having a couple of little issues navigating how to generate a GUI (in context of a Python script) despite reading the documentation and Stack Overflow answers. My objective is to create a frame with 7 radio button choices each corresponding to a screen resolution size which when selected and the submit button is pressed, the selected radio button will pass its value to a variable. However when I implement my GUI, I get two issues.
The first is that my frame opens correctly with the radio buttons, but another frame, which is blank and is titled "tk" appears. Regardless of what I do (i.e. use root.withdraw() etc. as others have mentioned), this blank window still appears.
The second and more baffling issue I am having is that when generated, all but the first radio button is selected, not normally with a dot in the center, but with a hyphen. Now the user can press on the option he/she wants and it will all unselect except for the choice, but it doesn't look normal and would probably confuse the user. I read about setting tristatevariable to none yet that didn't work (or at least in my trial). I also tried to force a deselect() function on all of the radio buttons before they generate and that didn't work either. Also, keep in mind that the radio buttons' variable must handle a string and not an int. What is happening here and how can I fix it?
The code snippet that pertains to both of these seemingly related issues is as follows:
if urldata == None:
class ResolutionInputGUI:
def __init__(self, master):
self.master = master
master.title("My GUI")
self.label = tk.Label(master, text="Your Screen Resolution Is: " + screenres + "\n")
self.label.pack()
MODES = [
("500×500", "500×500"),
("1280×800", "1280×800"),
("1280×1024", "1280×1024"),
("1440×900", "1440×900"),
("1680×1050", "1680×1050"),
("1920×1080", "1920×1080"),
("1920×1200", "1920×1200")
]
resolution = tk.StringVar()
resolution.set("500×500")
for text, mode in MODES:
self.radiobutton = tk.Radiobutton(master, text=text, variable=resolution, value=mode)
self.radiobutton.pack(anchor=tk.W)
self.submit_button = tk.Button(master, text="Submit", command=self.submit)
self.submit_button.pack()
self.cancel_button = tk.Button(master, text="Cancel", command=self.cancelbutton)
self.cancel_button.pack()
def submit(self):
global screenres
screenres = self.radiobutton.get()
root.quit()
self.master.destroy()
print(screenres)
def cancelbutton(self):
raise SystemExit
root = tk.Tk()
my_gui = ResolutionInputGUI(root)
root.mainloop()
Any help would be greatly appreciated as I cant seem to solve this issue and tkinter seems to be much more complicated than originally thought. Also, is there anything else that I am doing inefficiently here or to make the end user experience more "friendly?" Thank you so much!
The first is that my frame opens correctly with the radio buttons, but another frame, which is blank and is titled "tk" appears
This is because you are calling Tk() twice. I see one of them near the end, and you must have another elsewhere in your code.
all but the first radio button is selected, not normally with a dot in the center, but with a hyphen.
This is because you are using a local variable. Change "resolution" to "self.resolution".
when selected and the submit button is pressed, the selected radio button will pass its value to a variable
To do this you need to return the value from the variable, not from the button.
Also, you should put the class definition at the global level.
import tkinter as tk
class ResolutionInputGUI:
def __init__(self, master):
self.master = master
master.title("My GUI")
self.label = tk.Label(master, text="Your Screen Resolution Is: " + screenres + "\n")
self.label.pack()
MODES = [
("500×500", "500×500"),
("1280×800", "1280×800"),
("1280×1024", "1280×1024"),
("1440×900", "1440×900"),
("1680×1050", "1680×1050"),
("1920×1080", "1920×1080"),
("1920×1200", "1920×1200")
]
self.resolution = tk.StringVar(master, value="500×500")
for text, mode in MODES:
self.radiobutton = tk.Radiobutton(master, text=text, variable=self.resolution, value=mode)
self.radiobutton.pack(anchor=tk.W)
self.submit_button = tk.Button(master, text="Submit", command=self.submit)
self.submit_button.pack()
self.cancel_button = tk.Button(master, text="Cancel", command=self.cancelbutton)
self.cancel_button.pack()
def submit(self):
global screenres
screenres = self.resolution.get()
root.quit()
self.master.destroy()
print(screenres)
def cancelbutton(self):
raise SystemExit
if urldata == None:
root = tk.Tk()
my_gui = ResolutionInputGUI(root)
root.mainloop()
I'm trying to make a tool window for Maya, in which I can right-click anywhere, and if I click 'add', a rectangle widget shows up at my cursor position.
Now my right-click functionality works. I can also get my cursor position in addPicker() function. But I am having problem with placing newly-created widgets. If I add a layout and add the newly-created widgets to it, they actually show up. However, if I didn't create a layout for those widgets, no matter what position I tested, nothing shows up in my window.
Hopefully someone has some ideas. Thank you all in advance.
A right-click screenshot:
class RightClickMenu(QtGui.QMenu):
def __init__(self, *args, **kwargs):
super(RightClickMenu, self).__init__(*args)
self.parentWidget().setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.parentWidget().customContextMenuRequested.connect(self.menuPos)
def menuPos(self, *args):
self.exec_(QtGui.QCursor.pos())
class Ui_window(object):
def setupUi(self, window):
window.setObjectName("window")
window.resize(555, 900)
self.widget_base = QtGui.QWidget()
self.verticalLayout_window = QtGui.QVBoxLayout(window)
self.verticalLayout_window.addWidget(self.widget_base)
self.menu_popup = RightClickMenu(self.widget_base)
self.menu_popup.setObjectName("popupMenu")
self.verticalLayout_widget = QtGui.QVBoxLayout(self.widget_base)
# Action - add picker
addAction = QtGui.QAction('Add Picker', self.widget_base)
addAction.setShortcut('Ctrl+A')
addAction.setStatusTip('Add Picker')
addAction.triggered.connect(self.addPicker)
self.menu_popup.addAction(addAction)
# Action - delete picker
deleteAction = QtGui.QAction('Delete Picker', self.widget_base)
deleteAction.setShortcut('Ctrl+D')
deleteAction.setStatusTip('Delete Picker')
deleteAction.triggered.connect(self.deletePicker)
self.menu_popup.addAction(deleteAction)
def addPicker(self):
cursorPos = QtGui.QCursor.pos()
localPos = self.widget_base.mapFromGlobal(cursorPos)
######################################################################
# how??? below doesn't work.
self.pushButton = QtGui.QPushButton(self.widget_base)
self.pushButton.setGeometry(QtCore.QRect(220, 50, 75, 23))
self.pushButton.setObjectName("pushButton")
def deletePicker(self):
print 'delete'
def run():
import sys
try:
Ui_window.close()
except:
pass
pickerWindow = QtGui.QDialog()
ui = Ui_window()
ui.setupUi(pickerWindow)
pickerWindow.show()
pickerWindow.exec_()
Surprising solution (see this question):
self.pushButton.show()