I'm trying to use wx.Listbook to implement a settings window with multiple pages. I made the Listbook just fine, but when I started adding items to a page, I ran into a problem. The items are displayed on top of each other, so it's impossible to see everything I'm trying to show.
self.global_settings_frame = wx.Frame(parent=self, title="Global Settings", name="Global Settings")
self.global_settings_listbook = wx.Listbook(parent=self.global_settings_frame, style=wx.LB_LEFT)
self.global_settings_file_window = wx.Panel(parent=self.global_settings_listbook)
self.global_settings_file_box = wx.BoxSizer(orient=wx.VERTICAL)
self.show_full_pathname_checkbox = wx.CheckBox(self.global_settings_file_window, label="Show full pathname")
self.global_settings_file_box.Add(self.show_full_pathname_checkbox, proportion=1)
self.global_default_extension = wx.TextCtrl(self.global_settings_file_window)
self.global_settings_file_box.Add(self.global_default_extension, proportion=1)
self.global_settings_token_window = wx.Panel(parent=self.global_settings_listbook)
self.global_settings_listbook.InsertPage(0, self.global_settings_file_window, "Files")
self.global_settings_listbook.InsertPage(1, self.global_settings_token_window, "Token Defnition")
self.global_settings_frame.Show()
When I comment out the second element, the uncommented part works fine:
self.global_settings_frame = wx.Frame(parent=self, title="Global Settings", name="Global Settings")
self.global_settings_listbook = wx.Listbook(parent=self.global_settings_frame, style=wx.LB_LEFT)
self.global_settings_file_window = wx.Panel(parent=self.global_settings_listbook)
self.global_settings_file_box = wx.BoxSizer(orient=wx.VERTICAL)
self.show_full_pathname_checkbox = wx.CheckBox(self.global_settings_file_window, label="Show full pathname")
self.global_settings_file_box.Add(self.show_full_pathname_checkbox, proportion=1)
self.global_default_extension = wx.TextCtrl(self.global_settings_file_window)
self.global_settings_file_box.Add(self.global_default_extension, proportion=1)
self.global_settings_token_window = wx.Panel(parent=self.global_settings_listbook)
self.global_settings_listbook.InsertPage(0, self.global_settings_file_window, "Files")
self.global_settings_listbook.InsertPage(1, self.global_settings_token_window, "Token Defnition")
self.global_settings_frame.Show()
But I think the BoxSizer isn't working right because when I comment out the previous line (the one adding the CheckBox to the BoxSizer), the display is the same.
I've tried using separate panels for each element and then putting those panels in the BoxSizer, but that also didn't work (I can show you what that looks like if necessary). So it looks like I'm not using the BoxSizer correctly, but I don't understand how I am supposed to use it in this case. What I want is a page of a ListBook that contains a CheckBox and a TextCtrl (for single line text entry). Can you help?
As I can see, you never assign any sizer to your page(s).
You should just do it :
......
self.global_settings_file_box.Add(self.global_default_extension, proportion=1)
self.global_settings_file_window.SetSizer(self.global_settings_file_box)
self.global_settings_token_window = wx.Panel(parent=self.global_settings_listbook)
......
Regards
Xav'
Related
I am making a UI in Maya with Python. My goal is to make the master checkbox be able to control other checkboxes so that when the master checkbox is checked, all the other checkboxes are checked and vice versa. The function is as same as the Game Exporter/Animation Clips section in Maya. You can add multiple clips and use the master checkbox to control all clips. Does anyone know how to do that with Python?
The example (image) of what I want to achieve:
I've tried to use changeCommand, onCommand, and offCommand to edit other checkboxes, but it didn't work.
The code is like this (clipOn is multiple checkboxes, and masterCheckbox is the master checkbox that can control others):
clipOn = cmds.checkBox(label = '')
masterCheckbox = cmds.checkBox(label = '', onCommand = lambda x: cmds.checkBox(clipOn, editable = True, onCommand = True))
It's late and this is untested, but something like the following should work. Maya's 'onCommand'/ 'offCommand' parameters require a string or command to be returned. I used enumerate with list comprehension so that it should return a string for each value as it loops through and edits the checkboxes (rather than a single list). Of course you can always just call a regular function, which is arguably an easier and more straightforward approach.
master = cmds.checkBox(label='All')
check1 = cmds.checkBox(label='One')
check2 = cmds.checkBox(label='Two')
check3 = cmds.checkBox(label='Three')
checkAll = lambda state: [cmds.checkBox(c, edit=1, value=state) for i, c in enumerate([check1,check2,check3])][i]
cmds.checkBox(master, edit=1, onCommand=checkAll(True), offCommand=checkAll(False))
I would have thought:
checkAll = lambda state: cmds.checkBox([check1, check2, check3], edit=1, value=state)
would have worked. But it appears the checkBox command doesn't allow you to pass in multiple objects.
By the way, if you want to edit the 'onCommand' cmds.checkBox(clipOn, editable = True, onCommand = True)), you would pass a new command in not a bool value. Lastly, there is a 'checkBoxGrp' MEL command that allows creating and editing groups of checkboxes, however ultimately, I don't think it does anything different then what you can accomplish with the standard 'checkBox' command.
I would advise something like this :
from functools import partial
clipList = []
def addClip(*args):
newClip = cmds.checkBox(label='clip', p=mainLayout)
global clipList
clipList.append(newClip)
def setAllClips(ckb_master, *args):
value = cmds.checkBox(ckb_master, q=True, v=True)
if clipList:
for clip in clipList:
cmds.checkBox(clip, e=True, v=value)
mainLayout = cmds.columnLayout()
master = cmds.checkBox(label='All', p=mainLayout, cc=partial(setAllClips, master))
addClip()
I'm trying to see if there's a way to type a number between 1 and 4 into an entry box, then go to the next entry box (with the number entered into the box; the code below skips to the next entry without entering anything)
I'm creating a program that will take item-level data entry to be computed into different subscales. I have that part working in different code, but would prefer not to have to hit tab in between each text entry box since there will be a lot of them.
Basic code:
from tkinter import *
master = Tk()
root_menu = Menu(master)
master.config(menu = root_menu)
def nextentrybox(event):
event.widget.tk_focusNext().focus()
return('break')
Label(master, text='Q1',font=("Arial",8)).grid(row=0,column=0,sticky=E)
Q1=Entry(master, textvariable=StringVar)
Q1.grid(row=0,column=1)
Q1.bind('1',nextentrybox)
Q1.bind('2',nextentrybox)
Q1.bind('3',nextentrybox)
Q1.bind('4',nextentrybox)
Label(master, text='Q2',font=("Arial",8)).grid(row=1,column=0,sticky=E)
Q2=Entry(master, textvariable=StringVar)
Q2.grid(row=1,column=1)
Q2.bind('1',nextentrybox)
Q2.bind('2',nextentrybox)
Q2.bind('3',nextentrybox)
Q2.bind('4',nextentrybox)
### etc for rest of questions
### Scale sums, t-score lookups, and report generator to go here
file_menu = Menu(root_menu)
root_menu.add_cascade(label = "File", menu = file_menu)
file_menu.add_separator()
file_menu.add_command(label = "Quit", command = master.destroy)
mainloop()
Thanks for any help or pointers!
The simplest solution is to enter the event keysym before proceeding to the next field.
In the following example, notice how I added a call to event.widget.insert before moving the focus:
def nextentrybox(event):
event.widget.insert("end", event.keysym)
event.widget.tk_focusNext().focus()
return('break')
I've been working on this for a while and I can't find any information about adding a row to a window. I seen it done with pyside2 and qt, witch would work but the users are using multiple versions of Maya (2016 = pyside, 2017=pyside2).
I want it like adding a widget in in pyside. I done it where adding a row is a function like add row 1, add row 2, and add row 3 but the script get to long. I need to parent to rowColumnLayout and make that unique in order to delete that later. Also I have to query the textfield in each row. Maybe a for loop that adds a number to the row? I really don't know but this is what I have so far:
from maya import cmds
def row( ):
global fed
global info
item=cmds.optionMenu(mygroup, q=True, sl=True)
if item == 1:
cam=cmds.optionMenu(mygroup, q=True, v=True)
fed=cmds.rowColumnLayout(nc = 1)
cmds.rowLayout(nc=7)
cmds.text(l= cam )
cmds.text(l=u'Frame Range ')
start = cmds.textField('textField3')
cmds.text(l=u' to ')
finish = cmds.textField('textField2')
cmds.button(l=u'render',c='renderTedd()')
cmds.button(l=u'delete',c='deleteRow()')
cmds.setParent (fed)
def deleteRow ():
cmds.deleteUI(fed, layout=True)
if item == 2:
print item
global red
cam1=cmds.optionMenu(mygroup, q=True, v=True)
red = cmds.rowColumnLayout()
cmds.rowLayout(nc=7)
cmds.text(l= cam1 )
cmds.text(l=u'Frame Range ')
start = cmds.textField('textField3')
cmds.text(l=u' to ')
finish = cmds.textField('textField2')
cmds.button(l=u'render',c='renderTedd()')
cmds.button(l=u'delete',c='deleteRow2()')
cmds.setParent (red)
def deleteRow2 ():
cmds.deleteUI(red, control=True)
def cameraInfo():
info=cmds.optionMenu(mygroup, q=True, sl=True)
print info
def deleteRow ():
cmds.deleteUI(fed, control=True)
def getCamera():
layers=pm.ls(type="renderLayer")
for layer in layers:
pm.editRenderLayerGlobals(currentRenderLayer=layer)
cameras=pm.ls(type='camera')
for cam in cameras:
if pm.getAttr(str(cam) + ".renderable"):
relatives=pm.listRelatives(cam, parent=1)
cam=relatives[0]
cmds.menuItem(p=mygroup,label=str (cam) )
window = cmds.window()
cmds.rowColumnLayout(nr=10)
mygroup = cmds.optionMenu( label='Colors', changeCommand='cameraInfo()' )
getCamera()
cmds.button(l=u'create camera',aop=1,c='row ()')
cmds.showWindow( window )
This is totally doable with cmds. The trick is just to structure the code so that the buttons in each row know and can operate on the widgets in that row; once that works you can add rows all day long.
To make it work you want to do two things:
Don't use the string form of callbacks. It's never a good idea, for reasons detailed here
Do use closures to make sure your callbacks are referring to the right widgets. Done right you can do what you want without the overhead of a class.
Basically, this adds up to making a function which generates both the gui items for the row and also generates the callback functions -- the creator function will 'remember' the widgets and the callbacks it creates will have access to the widgets. Here's a minimal example:
def row_test():
window = cmds.window(title='lotsa rows')
column = cmds.columnLayout()
def add_row(cameraname) :
cmds.setParent(column)
this_row = cmds.rowLayout(nc=6, cw6 = (72, 72, 72, 72, 48, 48) )
cmds.text(l= cameraname )
cmds.text(l=u'Frame Range')
start = cmds.intField()
finish = cmds.intField()
# note: buttons always fire a useless
# argument; the _ here just ignores
# that in both of these callback functions
def do_delete(_):
cmds.deleteUI(this_row)
def do_render(_):
startframe = cmds.intField(start, q=True, v=True)
endframe = cmds.intField(finish, q=True, v=True)
print "rendering ", cameraname, "frames", startframe, endframe
cmds.button(l=u'render',c=do_render)
cmds.button(l=u'delete',c=do_delete)
for cam in cmds.ls(type='camera'):
add_row(cam)
cmds.showWindow(window)
row_test()
By defining the callback functions inside of add_row(), they have access to the widgets which get stored as start and finish. Even though start and finish will be created over and over each time the function runs, the values they store are captured by the closures and are still available when you click a button. They also inherit the value of cameraname so the rendering script can get that information as well.
At the risk of self-advertising: if you need to do serious GUI work using cmds you should check out mGui -- a python module that makes working with cmds gui less painful for complex projects.
I've been trying for days to find a way to group RadioToolButtons in pygobject without success. There is no *.RadioToolButton.join_group(*) method like RadioButtons.
Here is what I've been trying:
## Toolbar
self.mainWindow.mainBox.mainToolbar = Gtk.Toolbar()
self.mainWindow.mainBox.mainToolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR)
self.mainWindow.mainBox.mainToolbar.set_style(Gtk.ToolbarStyle.BOTH)
self.mainWindow.mainBox.mainToolbar.radioGroup = list() # *.radioGroup = [] Does not work either.
## Left toolbar separator
self.mainWindow.mainBox.mainToolbar.leftSeparator = Gtk.SeparatorToolItem(draw = False)
self.mainWindow.mainBox.mainToolbar.leftSeparator.set_expand(True)
## Overview toggle button
self.mainWindow.mainBox.mainToolbar.overviewRadio = Gtk.RadioToolButton(Gtk.STOCK_HOME)
self.mainWindow.mainBox.mainToolbar.overviewRadio.set_group(self.mainWindow.mainBox.mainToolbar.radioGroup)
self.mainWindow.mainBox.mainToolbar.overviewRadio.set_is_important(True)
self.mainWindow.mainBox.mainToolbar.overviewRadio.set_label("Overview")
self.mainWindow.mainBox.mainToolbar.overviewRadio.connect("clicked", self.on_overviewRadio_clicked)
self.mainWindow.mainBox.mainToolbar.overviewRadio.set_border_width(4)
## Basic settings toggle button
self.mainWindow.mainBox.mainToolbar.basicRadio = RadioToolButton(Gtk.STOCK_PROPERTIES)
self.mainWindow.mainBox.mainToolbar.basicRadio.set_group(self.mainWindow.mainBox.mainToolbar.radioGroup)
self.mainWindow.mainBox.mainToolbar.basicRadio.set_is_important(True)
self.mainWindow.mainBox.mainToolbar.basicRadio.set_label("Basic")
self.mainWindow.mainBox.mainToolbar.basicRadio.connect("clicked", self.on_basicRadio_clicked)
self.mainWindow.mainBox.mainToolbar.basicRadio.set_border_width(4)
## Right toolbar separator
self.mainWindow.mainBox.mainToolbar.rightSeparator = Gtk.SeparatorToolItem(
draw = False)
self.mainWindow.mainBox.mainToolbar.rightSeparator.set_expand(True)
(Not all of my code - *.show_all() is not the issue)
Here is what I get:
What am I doing wrong? How can I group these two buttons?
Create the second radio button so it's in the first radio button's group using:
Gtk.RadioToolButton.new_with_stock_from_widget(first_button, Gtk.STOCK_PROPERTIES)
PS. Looks like the UI task you are trying to accomplish might be better done with a Notebook?
I build PDF using ReportLab. My program has a MyDocTemplate(SimpleDocTemplate) class with two methods: beforePage(self) and afterPage(self) which add header and footer (as PNG image) on every page. There is also a MyDocStyle class which describe ParagraphStyle.
Main method looks like this:
TITLE = Paragraph(Title, MyDocStyle.h1)
TO = Paragraph(To, MyDocStyle.h2)
FROM = Paragraph(From, MyDocStyle.h2)
SUBJECT = Paragraph(Subject, MyDocStyle.h2)
LONG_PARAGRAPH = Paragraph(Text, MyDocStyle.h3)
...
Elements = [TITLE, TO, FROM, SUBJECT, LONG_PARAGRAPH, ...]
doc = MyDocTemplete('output.pdf', pagesize=A4,
leftMargin=2*cm, rightMargin=2*cm,
topMargin=4*cm, bottomMargin=4*cm)
doc.build(Elements)
Data comes from CSV files and GUI. From time to time (depends on data length) I receive an error:
Flowable <Spacer at 0x2631120 frame=normal>...(1 x 5.66929133858) too large
on page 1 in frame 'normal'(469.88976378 x 603.118110236) of template 'First'
This exception stop my program. For short Paragraphs I set in MyDocStyle class h2.keepWithNext = 1 however it's not perfect solution. ReportLab split correctly long paragraph if end of paragraph does not "coincide" with end of page (text area).
How can I deal with it?
This error occurs when ReportLab try to split a Spacer over two pages. It seems that the only way to workaround this issue is wrap your Spacer into a KeepTogether element:
elements.append(KeepTogether(Spacer(width, height)))
Solved. Don't use Spacer (e.g. Spacer(1, 0.2*cm)) as a separator for Paragraph. Instead, define spaceBefore and spaceAfter in ParagraphStyle, for example:
ParagraphStyle(name = 'Normal',
fontName = "Verdana",
fontSize = 11,
leading = 15,
alignment = TA_JUSTIFY,
allowOrphans = 0,
spaceBefore = 20,
spaceAfter = 20,
wordWrap = 1)