tk.Entry validate command doesn't restore previous value when False returned - validation

I have carefully reviewed answers to Interactively validating Entry widget content in tkinter, but my script fails to restore previous value if the validate command returns False. I captured %P and %s and print them out...They both show the same value.
import tkinter as tk
class Controller :
def __init__(self) :
i=10
j=20
# list comprehension
self.entry_widgets = [[None for col in range(j)] for row in range(i)]
#print(self.entry_widgets)
self.values = [["string"+str(row) + str(col) for col in range(10)] for row in range(20)]
#print(self.values)
class EnterBox(tk.Entry):
def __init__(self,*args,**kwargs):
#print (args)
self._modified = False
self._save = 0
self._raise = 1
self._lower = 2
frame, i,j, *newargs = args
self._content = tk.StringVar()
# tk.Entry.__init__(self,frame,*newargs,
# validate = 'focusout',
# validatecommand = vcmd,
# **kwargs)
tk.Entry.__init__(self,frame,*newargs,**kwargs)
vcmd = (self.register(self._revert), '%P', '%s')
ct.entry_widgets[i][j] = self
self.config(textvariable=self._content)
self.config(validate = "focusout")
self.config(validatecommand = vcmd )
x=(ct.values[i][j])
self.insert(0,x)
#self._content.set(x)
self.bind("<Return>",lambda event, x=self._save : self._action(event,x) )
self.bind("<Button-2>",lambda event, x=self._save : self._action(event,x) )
self.bind("<FocusIn>", lambda event, x=self._raise : self._action(event,x))
self.bind("<FocusOut>", lambda event, x=self._lower : self._action(event,x))
self.bind('<Button-3>', lambda event, x=self._lower : self._action(event,x))
self.grid(column=i+1,row=j+2)
def _revert(self,P,s):
print ("Hi There")
print(P)
print(s)
return False
def _action(self,event,action):
print(str(action)+' ' + str(event))
if action == self._save :
ct.values[i][j] = self._content.get()
self.config(bg='lightskyblue2')
self._modified = True
elif action == self._raise :
self.config(bg = 'light pink')
elif action == self._lower :
self.config(bg = 'gray80')
self._modified = False
else :
print('action value is bad action =>' + str(action))
if "__main__" == __name__ :
root = tk.Tk()
frame = tk.Frame()
i=j=0
ct = Controller()
root.grid()
frame.grid()
check = EnterBox(frame,i,j,width = 24)
check2 = EnterBox(frame,i+1,j,width = 24)
root.mainloop()
I have tried removing all other bindings, to no avail.
Interestingly, but a separate issue, If I use StringVar. set instead of self.insert, (see commented out line) the validate command runs once, and never again despite several focus changes. Using Python 3.8

The validation isn't designed to restore anything if the validation happens on focusout. The validation can only prevent characters from being added at the time they are added. You will have to add code to restore the previous value.

Related

pystray on MacOS to use run_detached, program crashed

I am trying to use pystray to create a icon on tasktray, it is working on windows but now I am building one for Mac. I need the program minimize to tasktray on run on background. so I need to use icon.run_detached() instead of icon.run().
However, it keep crashing the app and I read the documents seems that I need to give some darwin_nsapplication = AppKit.NSApplication.sharedApplication() to the code but I really don't know how to implement this. here is my code.
import tkinter as tk
import time
import pystray
from tkinter import *
from tkinter import messagebox
from PIL import Image
import AppKit
`class Gui():
def __init__(self):
self.window = tk.Tk()
self.darwin_nsapplication = AppKit.NSApplication.sharedApplication()
self.image = Image.open("./images/noname.png")
self.menu = (
pystray.MenuItem('Show', self.show_window),
pystray.MenuItem('Quit', self.quit_window)
)
# Declaration of variables
self.hour=StringVar()
self.minute=StringVar()
self.second=StringVar()
# setting the default value as 0
self.hour.set("00")
self.minute.set("00")
self.second.set("00")
# Use of Entry class to take input from the user
hourEntry= Entry(self.window, width=3, font=("Arial",18,""),
textvariable=self.hour)
hourEntry.place(x=80,y=20)
minuteEntry= Entry(self.window, width=3, font=("Arial",18,""),
textvariable=self.minute)
minuteEntry.place(x=130,y=20)
secondEntry= Entry(self.window, width=3, font=("Arial",18,""),
textvariable=self.second)
secondEntry.place(x=180,y=20)
# button widget
btn = Button(self.window, text='Set Time Countdown', bd='5',
command= self.submit)
btn.place(x = 70,y = 120)
def submit(self):
try:
# the input provided by the user is
# stored in here :temp
temp = int(self.hour.get())*3600 + int(self.minute.get())*60 + int(self.second.get())
except:
print("Please input the right value")
while temp >-1:
# divmod(firstvalue = temp//60, secondvalue = temp%60)
mins,secs = divmod(temp,60)
# Converting the input entered in mins or secs to hours,
# mins ,secs(input = 110 min --> 120*60 = 6600 => 1hr :
# 50min: 0sec)
hours=0
if mins >60:
# divmod(firstvalue = temp//60, secondvalue
# = temp%60)
hours, mins = divmod(mins, 60)
# using format () method to store the value up to
# two decimal places
self.hour.set("{0:2d}".format(hours))
self.minute.set("{0:2d}".format(mins))
self.second.set("{0:2d}".format(secs))
# updating the GUI window after decrementing the
# temp value every time
self.window.update()
time.sleep(1)
# when temp value = 0; then a messagebox pop's up
# with a message:"Time's up"
if (temp == 0):
messagebox.showinfo("Time Countdown", "Time's up ")
# after every one sec the value of temp will be decremented
# by one
temp -= 1
def quit_window(self):
self.icon.stop()
self.window.destroy()
def show_window(self):
self.icon.stop()
self.window.protocol('WM_DELETE_WINDOW', self.withdraw_window)
self.window.after(0, self.window.deiconify)
def withdraw_window(self):
self.window.withdraw()
self.icon = pystray.Icon("name", self.image, "title", self.menu)
self.icon.run_detached()
if __name__ in '__main__':
app = Gui()
app.window.protocol('WM_DELETE_WINDOW', app.withdraw_window)
app.window.mainloop()`
I tried to add darwin_nsapplication to icon like self.icon = pystray.Icon("name", self.image, "title", self.menu,self.darwin_nsapplication)
But it is said 6 arguments are given, 2-5 are needed.

probleme de renitialisation d'un QtreeWidget

I am developing an application that allows to place orders with python and QtDesigner. I can't manage to place two commands in a row. The first command passes without any problem but when I want to place another command without closing the application, this error is displayed: "self.ui.treeWidgetcommand.topLevelItem(self.Line ).setText(0, str(Id))
AttributeError: 'NoneType' object has no attribute 'setText'".
def AddCommande(self):
QtWidgets.QTreeWidgetItem(self.ui.treeWidgetcommande)
Libelle = self.ui.comboBoxproduit.currentText()
Qte = int(self.ui.lineEditQteproduit.text())
Info = self.stock.GetProductName(Libelle)[0]
Id = str(int(Info[0]))
Pu = Info[1]
Total = int(Qte)*int(Pu)
data=(Libelle,Qte,Id,Pu,Total)
#print(data)
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(0, str(Id))
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(1, str(Libelle))
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(2, str(Qte))
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(3, str(Pu))
self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setText(4, str(Total))
self.Ligne +=1
def ValiderCommande(self):
Client = self.ui.comboBoxclient.currentText()
IdClient = self.stock.GetClientIdByName(Client.split(" ")[0])
PrixTotal = 0
UniqueId = random.random()
Date = date.today()
Data = (IdClient,PrixTotal,Date,UniqueId)
if self.stock.AddCommande(Data) == 0:
for i in range(self.Ligne):
IdCommande = self.stock.GetClientIdByUniqueId(UniqueId)
Libelle = self.ui.treeWidgetcommande.topLevelItem(i).text(1)
IdProduit = self.ui.treeWidgetcommande.topLevelItem(i).text(0)
Pu = self.ui.treeWidgetcommande.topLevelItem(i).text(3)
Qte = self.ui.treeWidgetcommande.topLevelItem(i).text(2)
Total = int(self.ui.treeWidgetcommande.topLevelItem(i).text(4))
InfoData = (IdCommande, Libelle, Qte, Pu, Total)
data = (Qte,IdProduit)
if self.stock.AjoutInfoCommande(InfoData) == 0:
PrixTotal += Total
self.stock.UpdateQteStock(data)
if self.stock.UpdateCommande(PrixTotal,IdCommande) == 0:
self.ui.treeWidgetcommande.clear()
#self.ui.treeWidgetcommande.topLevelItem(self.Ligne).setHidden(True)
self.ui.lineEditQteproduit.setText(" ")
`
I would like after placing an order, reset my treeWidget array and be able to place other orders.

update_cells not working as expected

I have written this function:
def duplicate_sheet1(wb, title=None):
if title is None:
title = wb.sheet1.title + ' DUPLICATE'
wb._sheet_list = [wb.sheet1]
wb.add_worksheet(title, wb.sheet1.row_count, wb.sheet1.col_count)
wb._sheet_list = wb._sheet_list[::-1]
wb._sheet_list[0].update_cells(wb._sheet_list[1]._fetch_cells())
...everything works as expected upon inspection with a debugger except update_cells, when I _fetch_cells for worksheet 0 after running the code, the sheet is empty.
Apparently the list returned by _fetch_cells is not the same as what is expected by update_cells. This may be because _fetch_cells does not include empty cells in the returned list, update_cells may only work with a 1 or 2-D grid--I am unsure.
Here is the work-around I found, apologies as the code could could probably be improved:
def duplicate_sheet1(wb, title=None):
if title is None:
title = wb.sheet1.title + ' DUPLICATE'
wb._sheet_list = [wb.sheet1]
wb.add_worksheet(title, wb.sheet1.row_count, wb.sheet1.col_count)
wb._sheet_list = wb._sheet_list[::-1]
cell_list = build_cell_list(wb._sheet_list[0], wb._sheet_list[1])
wb._sheet_list[0].update_cells(cell_list)
def build_cell_list(new_worksheet, old_worksheet):
fetched = old_worksheet._fetch_cells()
max_row = fetched[-1].row
max_col = max([cell.col for cell in fetched])
cell_list = new_worksheet.range('A1:' + chr(max_col + 64) + str(max_row))
for cell in cell_list:
cell.value = next(
(
f.value for f in fetched
if f.col == cell.col and f.row == cell.row
),
'',
)
return cell_list

While debugging the function breakpoint is not hit

I am using powerbuilder 11.2 and I have a pbl that creates a main screen. The user enters an order number in the textbox and hit enter and it fills in data in the bottom of the screen. I am trying to get debugging to hit a breakpoint in my function but it seems to ignore the breakpoint. Is there a way to break into the function? I have a variable I need to evaluate and I can't seem to get into the function while running. Here is the code:
Decimal{2} ld_total_hrs,ld_load_hrs,ld_unload_hrs
long ll_stops_rowcount, ll_row, ll_type, ll_ord_number, ll_rd_rowcount
datetime ldt_1st_stop, ldt_last_stop, ldt_start_time, ldt_end_time, ldt_deliver_time
string ls_dest_id, ls_type, ls_pay_id, ls_ref_number, ls_pay_leg_config
boolean lb_first_drop = TRUE
ld_load_hrs = 0
ld_unload_hrs = 0
SetNull(ldt_deliver_time)
ll_stops_rowcount = dw_trip.RowCount()
If ll_stops_rowcount < 1 then Return 0
For ll_row = 1 to ll_stops_rowcount
If ll_row = 1 then
ldt_1st_stop = dw_trip.GetItemDateTime ( 1, "stops_stp_arrivaldate" )
End if
If dw_trip.GetItemString(ll_row,"stops_stp_type") = "PUP" then
ldt_start_time = dw_trip.GetItemDateTime(ll_row,"stops_stp_arrivaldate")
ldt_end_time = dw_trip.GetItemDateTime(ll_row,"stops_stp_departuredate")
ld_load_hrs = ld_load_hrs + (f_datetimediff(ldt_start_time,ldt_end_time)/60)/60
End if
If dw_trip.GetItemString(ll_row,"stops_stp_type") = "DRP" then
ldt_start_time = dw_trip.GetItemDateTime(ll_row,"stops_stp_arrivaldate")
ldt_end_time = dw_trip.GetItemDateTime(ll_row,"stops_stp_departuredate")
ld_unload_hrs = ld_unload_hrs + (f_datetimediff(ldt_start_time,ldt_end_time)/60)/60
// get the first drops info for the report if paylegaslane is true else get last drop
ls_pay_leg_config = is_PayLegConfig
If is_CompanyOverride=true then
ls_pay_leg_config = "ByLeg"
End if
//TGRIFFIT - PayLegConfig = 'ByLeg' in TMW is equivalent to 'PayLegAsLane = 'Y' in FSS
if Upper(ls_pay_leg_config) = 'BYLEG' then
If lb_first_drop Then
ldt_deliver_time = ldt_end_time
ls_dest_id = dw_trip.GetItemString(ll_row,"stops_cmp_id")
ls_ref_number = dw_trip.GetItemString(ll_row,"stops_stp_refnum")
lb_first_drop = FALSE
End if
else
ldt_deliver_time = ldt_end_time
ls_dest_id = dw_trip.GetItemString(ll_row,"stops_cmp_id")
ls_ref_number = dw_trip.GetItemString(ll_row,"stops_stp_refnum")
end if
End if
Next
ldt_last_stop = dw_trip.GetItemDateTime ( ll_stops_rowcount, "stops_stp_departuredate" )
ld_total_hrs = (f_datetimediff(ldt_1st_stop,ldt_last_stop)/60)/60
ll_ord_number = long(dw_triptab.GetItemString(1,"ord_number"))
//If g_messlevel% < 1 Then
if gnv_app.ii_MessLevel < 1 Then
ids_revdist.Reset()
End if
//Load the datastore that stores all the revenue distribution values
ll_rd_rowcount = ids_revdist.RowCount()
ids_revdist.InsertRow(0)
ll_rd_rowcount ++
ids_revdist.SetItem(ll_rd_rowcount,"mov_number",i_movenum%)
ids_revdist.SetItem(ll_rd_rowcount,"lgh_number",dw_trip.GetItemNumber(1,"stops_lgh_number"))
ids_revdist.SetItem(ll_rd_rowcount,"total_hours",ld_total_hrs)
ids_revdist.SetItem(ll_rd_rowcount,"load_hours",ld_load_hrs)
ids_revdist.SetItem(ll_rd_rowcount,"unload_hours",ld_unload_hrs)
ids_revdist.SetItem(ll_rd_rowcount,"deliver_date",ldt_deliver_time)
ids_revdist.SetItem(ll_rd_rowcount,"dest_code",ls_dest_id)
ids_revdist.SetItem(ll_rd_rowcount,"ref_number",ls_ref_number)
Return ids_revdist.RowCount()
I need to evaluate this line specifically and I set a breakpoint at this line:
ls_pay_leg_config = "ByLeg"
as well as the following lines. It does not break. I am rusty at PowerBuilder and can figure this out.
Put the breakpoint at the start of the loop.

How to override the key search in a treectrl?

i wanna know how to override the default keysearch in a treectrl.
When i bind a method to the EVT_TREE_KEY_DOWN event and call the selectItem method of the treectrl, it doesn't have any effect.
This is my Tree:
Test <--root
-Aero orea(EI)
-Blub(BL)
-Test(AX)
-123(45)
-Blib (LOL)
My intention:
With the keydown event i am concatenating a searchstring. when iterating over the treeitems, i split the names to get the content of the brackets(e.g.:"EI", "BL"...).
Then i check if the content of the brackets starts with my searchstring. if it is true the selectItem(TreeItemId) is called. But this won't work. It seems that the default search ist still working and is causing problems in my keysearch.
class MeinTreeCtrl(wx.TreeCtrl):
def __init__(self, parent):
wx.TreeCtrl.__init__(self, parent, -1)
root = self.AddRoot("test")
self.AppendItem(root, "Aero orea(EI)")
self.AppendItem(root, "Blub(BL)")
self.AppendItem(root, "Test(AX)")
self.AppendItem(root, "123(45)")
self.AppendItem(root, "Blib(LOL)")
self.searchString = ""
self.lastKeyDown = time.time()
parent.Bind(wx.EVT_TREE_KEY_DOWN, self.OnTreeKeySearch, self)
def GetItem(self, match, root):
item = self.GetFirstChild(root)
while item.IsOk():
tmp = self.GetItemText(item)
tmp = tmp.split(")")
tmp = tmp[len(tmp) - 2]
tmp = tmp.split("(")
tmp = tmp[len(tmp) - 1]
if tmp.startswith(match):
self.SelectItem(item)
break
item = self.GetNextChild(root, item)
return False
def OnTreeKeySearch(self, event):
now = time.time()
if self.searchString == "":
self.searchString = chr(event.GetKeyCode())
if (now - self.lastKeyDown) < 3:
self.searchString += str(chr(event.GetKeyCode()))
else:
self.searchString = str(chr(event.GetKeyCode()))
self.lastKeyDown = now
self.GetItem(self.searchString, self.GetRootItem())
Do you have any clue?
Thank you and best regards
Thomas
After a few days of search i found my mistake.
This line was the problem:
parent.Bind(wx.EVT_TREE_KEY_DOWN, self.OnTreeKeySearch, self)
First I need just the EVT_KEY_DOWN not the tree event for key down.
Second I binded the method to my parent not to self (That is because i did copy & paste :( )
This statement was really useful to me:
A typical example of an event not propagated is the wx.EVT_KEY_DOWN.
It is send only to the control having the focus, and will not
propagate to its parent.
-- EventPropagation

Resources