import filecmp
import os
import tkinter as tk
import tkinter.font as tkFont
from tkinter import *
def gg():
disk = (number_entry.get())
if disk == "2" :
DIRS = [r'C:\Newtest\1', r'C:\Newtest\2', r'C:\Newtest\3',r'C:\Newtest\4',r'C:\Newtest\5',r'C:\Newtest\6',r'C:\Newtest\7',r'C:\Newtest\8',r'C:\Newtest\9',r'C:\Newtest\10']
FILES = [('copy1.txt', 'copy2.txt'), ('fcmp1.txt', 'fcmp2.txt'), ('filecp1.txt', 'filecp2.txt')]
for e, dir in enumerate(DIRS, 1):
cmp = True
for file in FILES:
try:
if not filecmp.cmp(os.path.join(dir, file[0]), os.path.join(dir, file[1])):
cmp = False
break
except Exception as ex:
print(f'Error -> {ex}', file=sys.stderr)
continue
x = (f'E_{e} compare {"pass" if cmp else "fail"}')
print(x)
result_label.configure(text=x,wraplength=300,font=fontExample3)
DIRS = [r'C:\Newtest\1', r'C:\Newtest\2', r'C:\Newtest\3',r'C:\Newtest\4',r'C:\Newtest\5',r'C:\Newtest\6',r'C:\Newtest\7',r'C:\Newtest\8',r'C:\Newtest\9',r'C:\Newtest\10']
FILES = [('copy1.txt', 'copy2.txt'), ('fcmp1.txt', 'fcmp2.txt'), ('filecp1.txt', 'filecp2.txt')]
for e, dir in enumerate(DIRS, 1):
cmp = True
window = tk.Tk()
window.title('XXX')
window.geometry('1200x700')
window.configure(background='Azure')
fontExample = tkFont.Font(family="Segoe UI", size=20)
fontExample2 = tkFont.Font(family="Segoe UI", size=10)
fontExample3 = tkFont.Font(family="Segoe UI", size=15)
header_label = tk.Label(window, text='XXXX',font=fontExample)
header_label.pack()
number_frame = tk.Frame(window)
number_frame.pack(side=tk.TOP)
number_label = tk.Label(number_frame, text='Number of disks',font=fontExample2)
number_label.pack(side=tk.LEFT)
number_entry = tk.Entry(number_frame)
number_entry.pack(side=tk.LEFT)
result_label = tk.Label(window)
result_label.pack()
calculate_btn = tk.Button(window, text='First calculate', command=gg,font=fontExample3)
calculate_btn.pack()
window.mainloop()
Dear All ,
I have the question in the line:
result_label.configure(text=x,wraplength=300,font=fontExample3)
I want the result can print complete like my console.
Consele result is :
E_1 compare pass
E_2 compare fail
E_3 compare pass
E_4 compare pass
E_5 compare pass
E_6 compare pass
E_7 compare pass
E_8 compare pass
E_9 compare pass
E_10 compare fail
But in the tkinter result is -> E_10 compare fail
Please tolerate me as a newbie in tkinter.
Thanks!
If you want to add to the text that is already on the label, you can do this:
result_label.configure(text=result_label['text'] + x + '\n')
result_label['text'] returns the current text of the widget (equivalent to result_label.cget('text'))
You also don't need to configure wraplength and font each time, just do it once when creating the label like this:
result_label = tk.Label(window, wraplenght=300, font=fontExample3)
Also:
I strongly advise against using wildcard (*) when importing something, You should either import what You need, e.g. from module import Class1, func_1, var_2 and so on or import the whole module: import module then You can also use an alias: import module as md or sth like that, the point is that don't import everything unless You actually know what You are doing; name clashes are the issue.
I strongly suggest following PEP 8 - Style Guide for Python Code. Function and variable names should be in snake_case, class names in CapitalCase. Have two blank lines around function and class declarations.
Related
I'm using tkinter to create an option menu, where choosing an option will call a function specific to each option. However, I'm unable to figure out exactly how to do that.
This is the code that is currently being used.
import pandas as pd
import os
import matplotlib.pyplot as plt
#below code imports file needed at the moment
import tkinter as tk
from tkinter import *
from tkinter import filedialog
import pandas as pd
import os
import matplotlib.pyplot as plt
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename() #will open file from any location, does not need to be in the same place as the code script
df = pd.read_csv(file_path)
df.rename(columns={'Unnamed: 0':'Type'}, inplace=True) #renames the first unnamed column to type (drug available (DA) or not available (NA)
df.dropna(how = 'all', axis = 1, inplace = True) #drops the empty column present in each dataset, only drops it if the whole column is empty
##plotting functions for both active and inactive pokes
def ActivePokes(df):
plt.rcParams["figure.figsize"] = (12,7.5)
df.plot()
plt.xticks(range(0,len(df.Type)), df.Type)
plt.ylabel("Number of Active Pokes")
plt.xlabel("Sessions")
plt.title("Number of Active Pokes vs Drug Availability")
plt.show()
def InactivePokes(df):
plt.rcParams["figure.figsize"] = (12,7.5)
df.plot()
plt.xticks(range(0,len(df.Type)), df.Type)
plt.ylabel("Number of Inactive Pokes")
plt.xlabel("Sessions")
plt.title("Number of Inactive Pokes vs Drug Availability")
plt.show()
def show(df):
if variable == options[1]:
button[command] = ActivePokes(df)
elif variable == options[2]:
button[command] = InactivePokes(df)
else:
print("Error!")
options = [ "Choose Option",
"1. Active pokes, Drug Available and No Drug Available sessions",
"2. Inactive pokes, Drug Available and No Drug Available sessions"]
button = Tk()
button.title("Dialog Window")
button.geometry('500x90')
variable = StringVar(button)
variable.set(options[0]) #default value, might change and edit as time passes
option = OptionMenu(button, variable, *options, command = show)
option.pack()
button.mainloop()
I know the show() function is where the issue lies, but I'm not entirely sure how to rectify it.
The other comments and answer address problems with creating two Tk objects and using .get() with a StringVar.
The command = show callback is passed the string value of the item chosen. In your show( df ) when called from the Optionmenu will have df equal to one of the options. It won't be a pandas dataframe. Pure tkinter example below.
import tkinter as tk
root = tk.Tk()
root.geometry( '100x100' )
var = tk.StringVar( value = 'Option A' )
def on_choice( chosen ):
""" The callback function for an Optionmenu choice.
chosen: The text value of the item chosen.
"""
print( chosen, end = " : " )
print( ' or from the StringVar: ', var.get() )
opt_list = [ 'Option A', 'Option B', 'Option C' ]
options = tk.OptionMenu( root, var, *opt_list, command = on_choice )
options.grid()
root.mainloop()
First problem, you have created a tk instance called root, and then another one called button,why? perhaps you want button to be a tk.Button and not a tk instance? Not sure what is the intention here.
second, what is it command variable you want to change for button? (button[command]). if button where a tk.button, then perhaps you wanted to do button['command'] = ..., however, if the intention is to call the pokes functions why not calling them right away?
third problem is here:
def show(df):
if variable == options[1]:
button[command] = lambda: ActivePokes(df)
elif variable == options[2]:
button[command] = lambda: InactivePokes(df)
else:
print("Error!")
change variable for variable.get()
So I have designed a program that allows you to open 3 CSV files, and translate them into json, and 'clean' them then merge them into one file.
The way i have designed it, is that on display, there are 3 buttons for loading each file, but i am wondering if there is a better design that shows only 1 button, which the user can load multiple files that are assigned to different variables, depending on the column headings in these csv files.
The problem that I had, is when you load one file, it overrides the previously loaded file.
For reference, the csv files can be only 1/3 different formats so the column headings are known.
As you can see from a short part of my code, that i have created 2 different loadfile buttons, for the 2 separate files, but is there a way i can have just 1 button, that assisgns different files to different variables?
import tkinter as tk
from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
import pandas as pd
import json
import csv
import sys
import sqlite3
#
def loadInspection():
global inspectionFile
global label_inspectionFile
loadingCSV = filedialog.askopenfilename()
inspectionFile = pd.read_csv(loadingCSV)
#Making buttons for load and translate and clean
loadButton = Button(window1, text="Load Inspections File", command=loadInspection)
loadButton.grid(row=0, column=1)
#loading first file called inspections
def inspectionClean():
global inspectionFile
inspectionFile = inspectionFile
#Check and remove any duplicates in the Inspection Data
inspectionFile = inspectionFile[inspectionFile.duplicated() == False]
cleanInsButton = Button(window1, text="Clean", command=inspectionClean)
cleanInsButton.grid(row=0, column=3)
#loading second file called inventroy
def loadInventroy():
global inventroyFile
loadingCSV = filedialog.askopenfilename()
inventroyFile = pd.read_csv(loadingCSV)
#Making buttons for load and translate and clean
loadButton = Button(window1, text="Load Inventroy File", command=loadInventroy)
loadButton.grid(row=3, column=1)
def inventroyClean():
global inventroyFile
inventroyFile = inventroyFile
#Check and remove any duplicates in the Inspection Data
inventroyFile = inventroyFile[inventroyFile.duplicated() == False]
cleanInvButton = Button(window1, text="Clean", command=inventroyClean)
cleanInvButton.grid(row=3, column=3)
My guess would be this may be a problem with global variables. In general I would recommend using instance/class variables instead, using global variables is generally not advised.
Your load functionality could instead be structured like this:
class MyCSVLoader:
def __init__(self):
self.inventory = None
self.inspection = None
def loadCSV(self):
loadingCSV = filedialog.askopenfilename()
return pd.read_csv(loadingCSV)
def loadAll(self):
self.inspection = self.loadCSV()
self.inventory = self.loadCSV()
However, let me know if this works as I don't have csv files on hand to test it with.
Then, you could implement it with one button like this:
csvLoader = MyCSVLoader()
loadButton = tk.Button(window1, text="Load All Files", command=csvLoader.loadAll)
loadButton.grid(row=0, column=1)
Which should pop up 1 window after the other for you to select 2 files. The files selected can then be accessed through csvLoader.inventory and csvLoader.inspection.
Note that your clean function would also have to go into this class to function properly.
I'm building a toolbox UI using python in Maya, and I keep on getting a Nonetype error when I call one of the imported functions. This is the script for the toolbox:
class Toolbox():
import maya.cmds as cmds
def __init__(self):
self.window_name = "mlToolbox"
def create(self):
self.delete()
self.window_name = cmds.window(self.window_name)
self.m_column = cmds.columnLayout(p = self.window_name, adj = True)
cmds.button(p=self.m_column,label = 'MyButton', c=lambda *arg: cmds.polySphere(r = 2))
cmds.button(p=self.m_column, label = 'Make_Control', command = lambda *args: self.ControlBTN())
cmds.button(p=self.m_column, label = 'Find Center of All Selected', command = lambda *args: self.CenterBTN())
cmds.button(p=self.m_column, label = 'Find Center of Each Selected Object', command = lambda *args: self.IndiCenterBTN())
self.colorname = cmds.textField(placeholderText = 'Enter color name...')
cmds.button(p=self.m_column, label = 'ChangeColor', command = lambda *args: self.colorBtn())
self.MinAndMax = cmds.textField()
cmds.button(p=self.m_column, label = 'Random Scatter', command = lambda *args: self.ScatterBTN())
cmds.showWindow(self.window_name)
cmds.button(p=self.m_column, label = 'Select Everything', command = lambda *args: self.selectBTN())
def CenterBTN(self):
import CenterSelected
CenterSelected.Locator()
def ScatterBTN(self):
import Scatter
value = cmds.textField(self.MinAndMax, q=True)
Scatter.RandomScatter(value)
cmds.intField(self.moveMin, self.moveMax, self.rotMin, self.rotMax, self.scaleMin, self.scaleMax, e=True, text='')
def IndiCenterBTN(self):
import ManySelected
ManySelected.LocatorMany()
def colorBtn(self):
import ColorControl
value = cmds.textField(self.colorname, q=True, text = True)
ColorControl.colorControl(value)
cmds.textField(self.colorname, e=True, text='')
def selectBTN(self):
import tools
tools.selectAll()
def delete(self):
if cmds.window(self.window_name, exists=True):
cmds.deleteUI(self.window_name)
def ControlBTN(self):
import CreateControl
CreateControl.createControl()
myTool = Toolbox()
myTool.create()
And this is the function that I'm having trouble with:
def RandomScatter(MinAndMax):
import random
import maya.cmds as cmds
Stuff = cmds.ls(sl=True)
i=0
for i in range(random.randint(1,100)):
Stuff.append(cmds.duplicate(Stuff))
cmds.move( (random.randint(MinAndMax[0], MinAndMax[1])), (random.randint(MinAndMax[0], MinAndMax[1])), (random.randint(MinAndMax[0], MinAndMax[1])), Stuff[i], absolute=True )
cmds.rotate( (random.randint(MinAndMax[2], MinAndMax[3])), (random.randint(MinAndMax[2], MinAndMax[3])), (random.randint(MinAndMax[2], MinAndMax[3])), Stuff[i], absolute=True )
cmds.scale( (random.randint(MinAndMax[4], MinAndMax[5])), (random.randint(MinAndMax[4], MinAndMax[5])), (random.randint(MinAndMax[4], MinAndMax[5])), Stuff[i], absolute=True )
i = i+1
RandomScatter() works fine as long as I call it on it's own using a RandomScatter([a, b, c, d, e, f]) format, but when I try to call it Toolbox(), I get "Scatter.py line 21: 'NoneType' object has no attribute 'getitem'" as an error. It also happens when I try to use the intField() command instead of textField(). The UI window builds just fine; the error only happens after I enter input into the text field and press the button that's supposed to call RandomScatter(). It seems like the input isn't making it to the MinAndMax list, so when it reaches "cmds.move( (random.randint(MinAndMax[0]," it can't find anything to put in the MinAndMax[0] slot, or any of the slots after that, but I can't figure out why. Does anyone have any advice?
I didn't test your code and didn't read it totally, but I can already say that your strange "lambda" usage doesn't make any sens.
lambda *args: self.ControlBTN()
this lambda execute self.ControlBTN during the cmds.button definition and provide to it a function which return None.
that like doing that:
self.ControlBTN()
function = def func(): return None
cmds.button(p=self.m_column, label = 'Make_Control', command=function)
I advise you to reread the documentation about the "python lambda".
replace this by:
cmds.button(p=self.m_column, label = 'Make_Control', command=self.ControlBTN)
...
def ControlBTN(self, *args)
...
That should help
good luck
As written self.MinAndMax is a text field; even if you get the value from it it'll be a string and you won't be able to index into it to get the individual items -- and your indexing will be thrown off if any of the numbers are negative or have decimals. The lazy solution is to use a FloatFieldGrp which lets you have 2-4 numberic inputs. It's a bit annoying to get at all of the values at once (see the way it's done below) but it will avoid many issues with trying to parse the text field.
Also, this line doesn't make sense in context:
cmds.intField(self.moveMin, self.moveMax, self.rotMin, self.rotMax, self.scaleMin, self.scaleMax, e=True, text='')
You're not seeing the error because it's failing in the previous line, but the first argument ought to be the name of an existing intField.
In any case, I'd refactor this a bit to keep the argument parsing out of the scatter function, so you can separate out the working logic from the UI parsing logic. It'll make it much easier to spot where things have gone off the rails:
import random
import maya.cmds as cmds
class TestGUI(object):
def __init__(self):
self.window = cmds.window()
self.layout = cmds.rowLayout(nc=3)
self.min_xyz = cmds.floatFieldGrp( numberOfFields=3, label='min', value1=-10, value2=-10, value3=-10 )
self.max_xyz= cmds.floatFieldGrp( numberOfFields=3, label='min', value1=10, value2=10, value3=10 )
cmds.button(label='scatter', c = self.scatter)
cmds.showWindow(self.window)
def scatter(self, *_):
selected = cmds.ls(sl=True)
if not selected:
cmds.warning("select something")
return
min_xyz = (
cmds.floatFieldGrp(self.min_xyz, q=True, v1=True),
cmds.floatFieldGrp(self.min_xyz, q=True, v2=True),
cmds.floatFieldGrp(self.min_xyz, q=True, v3=True)
)
max_xyz = (
cmds.floatFieldGrp(self.max_xyz, q=True, v1=True),
cmds.floatFieldGrp(self.max_xyz, q=True, v2=True),
cmds.floatFieldGrp(self.max_xyz, q=True, v3=True)
)
print "scatter settings:", min_xyz, max_xyz
rand_scatter(selected, min_xyz, max_xyz)
def rand_scatter(selection, min_xyz, max_xyz):
dupe_count = random.randint(1, 10)
duplicates = [cmds.duplicate(selection) for n in range(dupe_count)]
for dupe in duplicates:
destination = [random.randint(min_xyz[k], max_xyz[k]) for k in range(3)]
cmds.xform(dupe, t=destination, absolute=True)
print (dupe, destination)
t = TestGUI() # shows the window; hit 'scatter' to test
My version changes your logic a bit -- you were duplicating your selection list, which would cause the number of items to grow exponentially (as each duplication would also duplicate the previous dupes). I'm not sure if that's inline with your intention or not.
You can avoid the extra lambdas by including a *_ in the actual button callback; it's a minor maya irritant that buttons always have that useless first argument.
As an aside, I'd try not to do imports inside of function bodies. If the imported module is not available, it's better to know at the time this file is imported rather than only when the user clicks a button -- it's much easier to spot a missing module if you do all your imports in a block at the top.
Dear Folks,
My python Combobox doesn't populate the windows drives. Please Help!!!
import tkinter as tk
from tkinter import ttk
from tkinter import font
import os.path
win = tk.Tk()
win.title("AR Duplicate File Search")
win.geometry("600x600")
win.configure(bg = 'green')
#----------------------------------Combo Box-----------------------------
dl = ['ABCDEFGHIJKLMNOPQRSTUVWXYZ']
drives = ['%s:' % d for d in dl if os.path.exists('%s:' % d)]
def convert(list):
return tuple(list)
listdrive = convert(drives)
search_loc_var = tk.StringVar()
search_loc_cmbbx1 = ttk.Combobox(win, width = 22, textvariable =
search_loc_var, state = 'readonly', values = drives)
# Defining the state readonly will restrict the user from typing anything
# in the combobox.
search_loc_cmbbx1['values'] = listdrive
search_loc_cmbbx1.grid(row = 2, column = 1)
win.mainloop()
I tried to populate the combobox from the tuple and list. But the combobox remains blank.
I suppose you want to populate your ttk.Combobox vertically with your existing drives on the local PC, such as :
C:
D:
E:
To provide this, just need to convert dl to an ordinary string as
dl = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
How to justify the values listed in drop-down part of a ttk.Combobox? I have tried justify='center' but that seems to only configure the selected item. Could use a resource link too if there is, I couldn't find it.
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
import tkinter.ttk as ttk
except ImportError:
import Tkinter as tk
import ttk
if __name__ == '__main__':
root = tk.Tk()
cbb = ttk.Combobox(root, justify='center', values=(0, 1, 2))
cbb.pack()
root.mainloop()
I have a one-liner solution. Use the .option_add() method after declaring ttk.Combobox. Example:
cbb = ttk.Combobox(root, justify='center', values=(0, 1, 2)) # original
cbb.option_add('*TCombobox*Listbox.Justify', 'center') # new line added
Here's one pure Python way that gets close to what you want. The items in the dropdown list all get justified to fit within the Combobox's width (or a default value will be used).
Update
Part of the reason the initial version of my answer wasn't quite right was because the code assumed that a fixed-width font was being used. That's not the case on my test platform at least, so I've modified the code to actually measure the width of values in pixels instead of whole characters, and do essentially what it did originally, but in those units of string-length measure.
import tkinter as tk
import tkinter.font as tkFont
from tkinter import ttk
class CenteredCombobox(ttk.Combobox):
DEFAULT_WIDTH = 20 # Have read that 20 is the default width of an Entry.
def __init__(self, master=None, **kwargs):
values = kwargs.get('values')
if values:
entry = ttk.Entry(None) # Throwaway for getting the default font.
font = tkFont.Font(font=entry['font'])
space_width = font.measure(' ')
entry_width = space_width * kwargs.get('width', self.DEFAULT_WIDTH)
widths = [font.measure(str(value)) for value in values]
longest = max(entry_width, *widths)
justified_values = []
for value, value_width in zip(values, widths):
space_needed = (longest-value_width) / 2
spaces_needed = int(space_needed / space_width)
padding = ' ' * spaces_needed
justified_values.append(padding + str(value))
kwargs['values'] = tuple(justified_values)
super().__init__(master, **kwargs)
root = tk.Tk()
ccb = CenteredCombobox(root, justify='center', width=10, values=('I', 'XLII', 'MMXVIII'))
ccb.pack()
root.mainloop()
(Edit: Note that this solution works for Tcl/Tk versions 8.6.5 and above. #CommonSense notes that some tkinter installations may not be patched yet,
and this solution will not work).
In Tcl ( I don't know python, so one of the python people can edit the question).
A combobox is an amalgamation of an 'entry' widget and a 'listbox' widget. Sometimes to make the configuration changes you want, you need to access the internal widgets directly.
Tcl:
% ttk::combobox .cb -values [list a abc def14 kjsdf]
.cb
% pack .cb
% set pd [ttk::combobox::PopdownWindow .cb]
.cb.popdown
% set lb $pd.f.l
.cb.popdown.f.l
% $lb configure -justify center
Python:
cb = ttk.Combobox(value=['a', 'abc', 'def14', 'kjsdf'])
cb.pack()
pd = cb.tk.call('ttk::combobox::PopdownWindow', cb)
lb = cb.tk.eval('return {}.f.l'.format(pd))
cb.tk.eval('{} configure -justify center'.format(lb))
Some caveats. The internals of ttk::combobox are subject to change.
Not likely, not anytime soon, but in the future, the hard-coded .f.l
could change.
ttk::combobox::PopdownWindow will force the creation of the listbox when it is called. A better method is to put the centering adjustment into
a procedure and call that procedure when the combobox/listbox is mapped.
This will run for all comboboxes, you will need to check the argument
in the proc to make sure that this is the combobox you want to adjust.
proc cblbhandler { w } {
if { $w eq ".cb" } {
set pd [ttk::combobox::PopdownWindow $w]
set lb $pd.f.l
$lb configure -justify center
}
}
bind ComboboxListbox <Map> +[list ::cblbhandler %W]
After digging through combobox.tcl source code I've come up with the following subclass of ttk.Combobox. JustifiedCombobox justifies the pop-down list's items almost precisely after1 pop-down list's been first created & customized and then displayed. After the pop-down list's been created, setting self.justify value to a valid one will again, customize the justification almost right after the pop-down list's first been displayed. Enjoy:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
from tkinter import ttk
except:
import Tkinter as tk
import ttk
class JustifiedCombobox(ttk.Combobox):
"""
Creates a ttk.Combobox widget with its drop-down list items
justified with self.justify as late as possible.
"""
def __init__(self, master, *args, **kwargs):
ttk.Combobox.__init__(self, master, *args, **kwargs)
self.justify = 'center'
def _justify_popdown_list_text(self):
self._initial_bindtags = self.bindtags()
_bindtags = list(self._initial_bindtags)
_index_of_class_tag = _bindtags.index(self.winfo_class())
# This dummy tag needs to be unique per object, and also needs
# to be not equal to str(object)
self._dummy_tag = '_' + str(self)
_bindtags.insert(_index_of_class_tag + 1, self._dummy_tag)
self.bindtags(tuple(_bindtags))
_events_that_produce_popdown = tuple([ '<KeyPress-Down>',
'<ButtonPress-1>',
'<Shift-ButtonPress-1>',
'<Double-ButtonPress-1>',
'<Triple-ButtonPress-1>',
])
for _event_name in _events_that_produce_popdown:
self.bind_class(self._dummy_tag, _event_name,
self._initial_event_handle)
def _initial_event_handle(self, event):
_instate = str(self['state'])
if _instate != 'disabled':
if event.keysym == 'Down':
self._justify()
else:
_ = self.tk.eval('{} identify element {} {}'.format(self,
event.x, event.y))
__ = self.tk.eval('string match *textarea {}'.format(_))
_is_click_in_entry = bool(int(__))
if (_instate == 'readonly') or (not _is_click_in_entry):
self._justify()
def _justify(self):
self.tk.eval('{}.popdown.f.l configure -justify {}'.format(self,
self.justify))
self.bindtags(self._initial_bindtags)
def __setattr__(self, name, value):
self.__dict__[name] = value
if name == 'justify':
self._justify_popdown_list_text()
def select_handle():
global a
_selected = a['values'][a.current()]
if _selected in ("left", "center", "right"):
a.justify = _selected
if __name__ == '__main__':
root = tk.Tk()
for s in ('normal', 'readonly', 'disabled'):
JustifiedCombobox(root, state=s, values=[1, 2, 3]).grid()
a = JustifiedCombobox(root, values=["Justify me!", "left", "center", "right"])
a.current(0)
a.grid()
a.bind("<<ComboboxSelected>>", lambda event: select_handle())
root.mainloop()
1 It basically makes use of bindtag event queue. This was mostly possible thanks to being able to creating a custom bindtag.