messagebox getting looped - python-2.7

I am displaying a message box on click of option menu when data is not available and is to be acquired. But somehow whenever I change content of the option menu the message box gets looped. Like- first time it vanishes when I press ok. when I change option again second time it displays message box two times and third time three times. Can someone tell me what to change in my code?
#the function where I change option menu value
def module_func(event):
#declarations
if(dateText.get()!="" and driveText.get()!=""):
global module_dir
global selected_module
modules['menu'].delete(0,END)
select="-----Select-----"
modules['menu'].add_command(label=select, command=tk._setit(module,select))
for module_dir in get_immediate_subdirectories(('%s\%s\%s')%(startPath,selected_shot,selected_fec)):
module_dropdown.append(module_dir)
modules['menu'].add_command(label=module_dir, command=tk._setit(module,module_dir))
module.trace('w',setEightText) #think problem is here
else:
errormsg("")
def setEightText(*args):
#declarations
if(os.path.exists(('%s\%s\%s\%s\%s')%(startPath,selected_shot,selected_fec,selected_module,chan))):
#some operations
else:
tkMessageBox.showinfo("Error","Data at given location not available.\n Please Wait until data is acquired.")
module=tk.StringVar(labelframe1)
module.set("-----Select-----")
modules = tk.OptionMenu(labelframe1, module, module_dropdown, command=module_func)
modules.config(width=20, background="SNOW")
modules.grid(row=8, column=2, sticky="W", padx=10, pady=5)
modules.bind('<Button-1>',module_func)
I think the problem is because of trace. But don't know what to add instead of that.

Related

PyQt5 QComboBox list items changing postion

I am facing some issue with the display style of Qcombobox items. Currently am hardcoding the data to be shown in the combobox.
here is the code :
self.Dummy = QComboBox(self)
self.Dummy.setGeometry(200,600, 350, 50)
self.Dummy.setStyleSheet("QComboBox {background-color: white;border-style: outset;" border-width: 2px;border-radius: 5px;border-color: #448aff; font: 12px; min-width: 10em; padding: 3px;}")
self.Dummy.addItems(["-Select-", "2", "3","4","5","6","7","8","9","0","11",])
The issue is that the dropdown "list" postion keeps changing after each selection. Here is the image of the issue am facing.
Below is my combobox
The list contains items <-Select->,2,3,4,5,6,7,8,9,0,11 , where <-Select-> will be the first element shown.
Now when I click the box, the box list "down" the elements and suppose I selected '2'. Then, if I try to select another item, the list will be dropped in a "downwards" direction. see below
Now, say if selected the last element from the items, '11'. Now if I try to select a new item by clicking on the box, the list will be popped "up" instead of down. see below
What should be done to fix it ? I don't think its an issue with stylesheet, without it also, this issue is happening. The reason I need this to be fixed is that when the list is popping up, its covering the label above it
What you see is a behavior that is OS and style dependent.
To avoid it, the best way is to subclass QComboBox and overwrite showPopup(), then we call the base class implementation (which is responsible of showing, resizing and positioning the popup view) and move it if necessary.
class Combo(QtWidgets.QComboBox):
def showPopup(self):
super().showPopup()
# find the widget that contains the list; note that this is *not* the view
# that QComboBox.view() returns, but what is used to show it.
popup = self.view().window()
rect = popup.geometry()
if not rect.contains(self.mapToGlobal(self.rect().center())):
# the popup is not over the combo, there's no need to move it
return
# move the popup at the bottom left of the combo
rect.moveTopLeft(self.mapToGlobal(self.rect().bottomLeft()))
# ensure that the popup is always inside the edges of the screen
# we use the center of the popup as a reference, since with multiple
# screens the combo might be between two screens, but that position
# could also be completely outside the screen, so the cursor position
# is used as a fallback to decide on what screen we'll show it
done = False
for i, pos in enumerate((rect.center(), QtGui.QCursor.pos())):
for screen in QtWidgets.QApplication.screens():
if pos in screen.geometry():
screen = screen.geometry()
if rect.x() < screen.x():
rect.moveLeft(screen.x())
elif rect.right() > screen.right():
rect.moveRight(screen.right())
if rect.y() < screen.y():
rect.moveTop(screen.y())
elif rect.bottom() > screen.bottom():
# if the popup goes below the screen, move its bottom
# *over* the combo, so that the its current selected
# item will always be visible
rect.moveBottom(self.mapToGlobal(QtCore.QPoint()).y())
done = True
break
if done:
break
popup.move(rect.topLeft())
This can also be done without subclassing (for example if you have many combos, you created the UI from Designer and don't want to use promoted widgets), but you'll have to remember to change all referencies to the combo.
class MyWindow(QtWidgets.QWidget):
def __init__(self):
# ...
self.combo = QtWidgets.QComboBox()
self.combo.showPopup = self.showPopupAndCheck
def showPopupAndCheck(self):
QtWidgets.QComboBox.showPopup(self.combo)
popup = self.view().window()
rect = popup.geometry()
if not rect.contains(self.combo.mapToGlobal(self.combo.rect().center())):
# the popup is not over the combo, there's no need to move it
return
# change everything from self to self.combo
Alternatively, if you want to keep this behavior consistent through all your program without always using the subclass, you can use some sort of monkey patching hack.
The advantage is that any QComboBox you create (even when loading UI files or creating a combo at runtime) will always use the new behavior.
Important: this MUST be at the very beginning of the main file of your program, possibly just after the import section.
from PyQt5 import QtCore, QtGui, QtWidgets
def customShowPopup(self):
# we can't use super(), because we're not in the class definition, but
# calling the class method with "self" as first argument is practically the
# same thing; note the underscore!
QtWidgets.QComboBox._showPopup(self)
popup = self.view().window()
# ... go on, exactly as above
# create a new reference to the showPopup method, which is the one we've used
# in the function above
QtWidgets.QComboBox._showPopup = QtWidgets.QComboBox.showPopup
# overwrite the original reference with the new function
QtWidgets.QComboBox.showPopup = customShowPopup

Behavioral discrepancy in Tkinter listbox, arrow keys vs. mouse click

My environment is Python 2.7, running on Windows 7.
I'm trying get a Tkinter Listbox to trigger a callback in response to the user changing the 'active' item (i.e. the item with focus). I'm using a binding to the <<ListboxSelect>> event to make this happen, and it's working -- sort of.
The callback itself is supposed to check what the new active item is, and carry out some processing accordingly. This logic operates the way I expect when I change the active item via the up/down arrow keys. But when I point & click on a new item instead, the code mistakenly identifies the prior active item as the current one.
Here's a stripped-down code sample that illustrates the behavior I'm getting:
import Tkinter as tk
#Root window
root = tk.Tk()
#Callback to show focus change
def updateDisplay(*args):
focusIndex = str(lb.index(tk.ACTIVE))
ctrlFI.set('Focus is at index '+focusIndex)
#Control variables
ctrlLB = tk.StringVar()
ctrlFI = tk.StringVar()
#Widgets
lb = tk.Listbox(root,
width=20, height=10,
relief=tk.FLAT,highlightthickness=0,
selectmode=tk.EXTENDED,
activestyle='dotbox',
listvariable=ctrlLB)
lbl = tk.Label(root,
justify=tk.LEFT, anchor=tk.W,
textvariable=ctrlFI)
lb.grid(row=0,column=0,sticky=tk.NW,padx=(5,0),pady=5)
lbl.grid(row=1,column=0,columnspan=2,sticky=tk.NW,padx=5,pady=5)
#Listbox binding to trigger callback
lb.bind('<<ListboxSelect>>',updateDisplay)
#Initializations to prep GUI
ctrlLB.set('Index0-entry Index1-entry Index2-entry Index3-entry Index4-entry')
ctrlFI.set('Ready')
#Begin app
tk.mainloop()
Here are the results when you use the arrow keys:
But here's what you get when you click with the mouse:
The information 'lags' one behind, showing the prior selection instead. (If you click the same item a second time, it 'catches up.')
So my questions are:
What is causing the discrepancy?
How do I fix it so the mouse click gives the right result?
The active item is not necessarily the same as the selected item. When you press the mouse down it changes the selected value but it does not change the active item. The active item only changes once you release the mouse button.
You should be able to see this by clicking and holding the mouse button over an item that is not currently selected. When you do, you'll see something like this:
In the above image, the active item is the one surrounded by a dotted outline. The selected item is in blue. When your code displays the 'focus', it's displaying the active element rather than the selected element.
If you want the selected item, you need to use curselection to get the index of the selected item. It returns a tuple, so in extended mode you need to get the first element that is returned (eg: lb.curselection()[0]). Be sure to handle the case where curselection returns an empty string.

wxpython how to set a bind event on a button which gets enable upon clicking on other button

which consist a combo box 4 buttons. Once i select an entry from combo box, it will enable a button upon clicking one button it enables the rest. I want to send a command once the buttons is enabled on clicking it.
Below is my code:
import wx
import xlrd
import os,sys,time
folderpath = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
print folderpath
site_lib_path = os.path.join(folderpath, "site_lib")
files = os.listdir(site_lib_path)
for file in files:
sys.path.append(os.path.join(site_lib_path, file))
from printercx import printercx
from resttestservice.resttestservice import UITest
class ui(wx.Frame):
"""
This Class will create a Sample Frame and Create Two Buttons on tha Panel.
"""
def __init__(self,parent,id):
"""
This Fucntion will create a Frame and a Panel which has Two buttons: "OK" and "Cancel"
"""
"""-----SALQE Connecttion-----------"""
self.connection = printercx.deviceConnection()
self.ui = UITest(self.connection)
"""-----------Window Bar Name------"""
wx.Frame.__init__(self,parent,id,'GEN-2 Tool',size=(600,500))
panel=wx.Panel(self)
"""-----------Heading-------"""
header_text = wx.StaticText(panel, label="GEN-2 Tool", pos=(250,30))
font = wx.Font(15, wx.DECORATIVE, wx.NORMAL, wx.BOLD)
header_text.SetFont(font)
wx.StaticLine(panel, pos=(10, 75), size=(690,3))
"""-----------Buttons-------"""
self.pre_button=wx.Button(panel,label="Precondition",pos=(50,250),size=(100,40))
self.act_button=wx.Button(panel,label="Action",pos=(450,250),size=(100,40))
self.pass_button=wx.Button(panel,label="Pass",pos=(50,350),size=(100,40))
self.fail_button=wx.Button(panel,label="Fail",pos=(450,350),size=(100,40))
"""-------------------------------Excel-------------------------------------------------------"""
self.mainList=[]
self.val_list=[]
dic={}
book=xlrd.open_workbook("Reference_Mapping.xlsx")
sheet=book.sheet_by_name("TestCases")
n_row= sheet.nrows-1
n_col=sheet.ncols
row=1
while row<=n_row:
smallList=[]
col=0
while col<n_col:
cel=sheet.cell(row,0)
if cel.value!="":
self.val_list.append(cel.value)
key=sheet.cell(0,col).value
val=sheet.cell(row,col).value
dic[key]=val
col+=1
smallList.append(dic.copy())
self.mainList.append(smallList)
row+=1
self.val_list= list(set(self.val_list))
"""-------------------------------------------------------------------------------------------------"""
"""-----------Combo Box with Text-------"""
text=wx.StaticText(panel, label="Test Case: ", pos=(150,130))
font = wx.Font(10,wx.DECORATIVE, wx.NORMAL, wx.BOLD)
text.SetFont(font)
self.val_list.insert(0, "Select")
self.combobox=wx.ComboBox(panel, value=self.val_list[0], pos=(300,130), choices=self.val_list,style=wx.CB_READONLY)
self.Bind(wx.EVT_COMBOBOX, self.onTestCaseSelection, self.combobox)
print "-----------"
def onTestCaseSelection(self,event):
if self.combobox.GetSelection()>0:
print self.combobox.GetValue()
"""---------- Compairing Key's values--------------"""
for each in range(len(self.mainList)):
for every in range(len(self.mainList[each])):
if self.mainList[each][every]['TC_ID']==self.combobox.GetValue():
if self.mainList[each][every]['Ref_ID_Pre']=="":
if self.mainList[each][every]['Ref_ID_Post']!="":
self.pre_button.Enable(False)
self.act_button.Enable(True)
self.Bind(wx.EVT_BUTTON,self.send_udw,self.act_button)
self.pass_button.Enable(True)
self.fail_button.Enable(True)
if self.mainList[each][every]['Ref_ID_Pre']!="":
if self.mainList[each][every]['Ref_ID_Post']=="":
self.pre_button.Enable(True)
self.Bind(wx.EVT_BUTTON,self.send_udw,self.pre_button)
self.act_button.Enable(False)
self.pass_button.Enable(False)
self.fail_button.Enable(False)
if self.mainList[each][every]['Ref_ID_Pre']!="":
if self.mainList[each][every]['Ref_ID_Post']!="":
action_button_cmd=self.mainList[each][every]['Ref_ID_Post']
self.pre_button.Enable(True)
self.Bind(wx.EVT_BUTTON,self.send_udw,self.pre_button)
self.act_button.Enable(False)
self.pass_button.Enable(False)
self.fail_button.Enable(False)
else:
self.disableAllControls(without=None)
def disableAllControls(self, without=None):
if without==None:
self.pre_button.Enable(False)
self.act_button.Enable(False)
self.pass_button.Enable(False)
self.fail_button.Enable(False)
def send_udw(self,event):
for each in range(len(self.mainList)):
for every in range(len(self.mainList[each])):
if self.mainList[each][every]['TC_ID']==self.combobox.GetValue():
if self.mainList[each][every]['Ref_ID_Pre']=="":
if self.mainList[each][every]['Ref_ID_Post']!="":
if self.act_button.IsEnabled()==True:
post_command=self.mainList[each][every]['Ref_ID_Post']
post_udw="ui_v3.move_to_state "+post_command+" 1"
self.connection.udw(post_udw)
if self.mainList[each][every]['Ref_ID_Pre']!="":
if self.mainList[each][every]['Ref_ID_Post']=="":
if self.pre_button.IsEnabled()==True:
post_command=self.mainList[each][every]['Ref_ID_Pre']
post_udw="ui_v3.move_to_state "+post_command+" 1"
self.connection.udw(post_udw)
if self.mainList[each][every]['Ref_ID_Pre']!="":
if self.mainList[each][every]['Ref_ID_Post']!="":
if self.pre_button.IsEnabled()==True:
post_command=self.mainList[each][every]['Ref_ID_Pre']
post_udw="ui_v3.move_to_state "+post_command+" 1"
print post_udw
self.connection.udw(post_udw)
"""----Enabling button---"""
self.pre_button.Enable(False)
self.act_button.Enable(True)
self.pass_button.Enable(True)
self.fail_button.Enable(True)
time.sleep(1)
I want to send a command once this button self.act_button.Enable(True) gets enabled.
You can bind the button's to events before you disable them. They aren't going to react to events (other than maybe mouse events) until you enable them. There is no reason to bind events when you enable the button.
If you want to call a function after the enabling process (i.e. self.act_button.Enable(True)), then just call the function right after that:
self.act_button.Enable(True)
self.myFunction(*args, **kwargs)
If you want to create some kind of custom event, then you'll want to look into how to use wx.PostEvent and wx.lib.newevent. The following resources might interest you as well:
https://wiki.wxpython.org/CustomEventClasses
https://wxpython.org/Phoenix/docs/html/events_overview.html
http://wiki.ozanh.com/doku.php?id=python:misc:wxpython_postevent_threading

[Python2.7 TKinter]How to gray out a option in optionmenu that has been selected by older instances?

Here's my code:
class DefaultServiceClassWidget(ServiceClassWidget):
def __init__(self, mpet_widget, sc_data, idx):
super(DefaultServiceClassWidget, self).__init__(mpet_widget, sc_data, idx, DefaultServiceClassRow.number)
delete_default_sc_button = Button(mpet_widget.listFrame,justify=LEFT,text="x",fg="red",command= lambda: mpet_widget.delete_sc(self.idx))
delete_default_sc_button.grid(column=4,row=DefaultServiceClassRow.number)
self.select_default_class_label = Label(mpet_widget.listFrame,anchor=W,justify=LEFT,text="Select a Class")
self.select_default_class_label.grid(column=0,row=DefaultServiceClassRow.number)
options = ["All","CS Registration","GPRS Attach","PDP Activation","SMS","Reset","USSD","LTE"]
self.menu_pick_a_class = OptionMenu(mpet_widget.listFrame, sc_data.get_name(), *options, command=lambda event: sc_data.set_id())
self.menu_pick_a_class.grid(column=1,row=DefaultServiceClassRow.number)
self.row = DefaultServiceClassRow.number
DefaultServiceClassRow.number = DefaultServiceClassRow.number+2
def delete(self):
DefaultServiceClassRow.number = DefaultServiceClassRow.number - 2
default_list = (list(self.mpet_widget.listFrame.grid_slaves(row=self.row)) +
list(self.mpet_widget.listFrame.grid_slaves(row=self.row+1)))
for l in default_list:
l.grid_remove()
What happens is that there's a button connected to this function, every time the button is clicked, this function gets called and created a new grid on the GUI. What I want is that if for example "SMS" in optionmenu is selected, the next time this function gets called, "SMS" option will be grayed out (i.e. each option can be only selected once in the program)
I've tried updating status of the selected option by using status = "disabled" but it only works for the same grid, once a new grid(instance) is created, everything gets reset and all the options became available again :(
Also, in the same grid, if I disabled an option by selecting it and then changed to something else, the original selection is still grayed and cannot be selected again - I know why this happens but how do I fix it?
Sorry for the long question but I can't seem to find a solution online :(
Thank you in advance!

Tkinter bind executes immediately when script is run

I've laid out a frame, part of which is:
ticketID = IntVar()
ticketID.set(ticket)
ticketfield = Label(titleframe, text = 'Ticket : ')
ticketfield.grid(row=0, column=0)
ticketfieldentry = Entry(titleframe, textvariable=ticketID)
ticketfieldentry.grid(row=0, column=1)
ticketfieldentry.bind("<Double-Button-1>", searchforticket(ticketfieldentry.get()))
And a placeholder:
def searchforticket(ticket):
searchforstring = "This would search for ticket %s" % ticket
tkMessageBox.showinfo('Search by ticket', message = searchforstring)
Ticket is passed to the script at run time, or is assumed None. What I thought the above would do is create an Entry box that would display the ticket number, or could have one entered directly. After being entered, double clicking would bring up the searchforticket function.
However, when I run the script, searchforticket is run immediately, with whatever is being assigned to ticket and then after I click past the tkMessageBox is when the window containing the ticketfieldentry and everything else renders.
I am not seeing at all what could be causing this, but I assume it is something dead obvious to someone else.
searchforticket(ticketfieldentry.get() cause immediate call. Change as follow:
ticketfieldentry.bind("<Double-Button-1>", lambda e: searchforticket(ticketfieldentry.get()))