I am building a “pseudo-intelligent” GUI for a gimp plugin using Glade. The main part of the GUI has two frames and imports the contents using the “reparent” method. The main objective is to have the contents of the second frame determined by the selections made in the first frame. (Eventually, the intention is to import this GUI as the content for the tabbed pages of a "notebook")
To start with, I made a simple window, consisting of a “RadioButtonBox” and a “ComboBox” which is populated using:
# create the cell renderer
self.cell = gtk.CellRendererText()
#populate the default choice into the Selection combobox
self.SelectionBox = self.builder.get_object("SelectionBox")
self.SelectionBox.set_model(self.EditCommands)
self.SelectionBox.pack_start(self.cell, True)
self.SelectionBox.add_attribute(self.cell, 'text', 1)
self.SelectionBox.set_active(0)
# End: populate the selection combo box section
This works and I can successfully “import” and “reparent” the simple GUI as the first frame of the larger, more complex GUI without any problems. However, as the design progressed, it has become more convenient to have the code for the first frame as an integral part of the main GUI, and this is where my problems begin.
I have reproduced the contents of the simple GUI in the first frame of the larger GUI and copy/pasted the code from the simple GUI's “init” function. In other-words, everything is identical.
Unfortunately, when I run the code I get the following error:
C:\Documents and Settings\anonymous\Desktop\Glade-tutorial\BatchEditMain\BatchEditLibrary\Tab.py:46: GtkWarning: gtk_entry_set_text: assertion `text != NULL' failed
self.SelectionBox.set_active(0)
Could someone please explain what the problem is?
Thanks in advance
Irvine
The GTK Warning is saying that somewhere gtk_entry.set_text() is being called with None instead of some text. This is happening in the call to self.SelectionBox.set_active(0)
This feels a little bit like gravedigging , but i stumbled across the same Problem today and this was the first post google showed...
With a little further research i found this Question:
How create a combobox on Python with GTK3?
The author seems to have the same error.
What in his and my case seems to do the trick is a simple:
combobox.set_entry_text_column(0)
Important it has to be in front of set_active(0)!
So in your case this would be:
...
self.SelectionBox.add_attribute(self.cell, 'text', 1)
self.SelectionBox.set_entry_text_column(0)
self.SelectionBox.set_active(0)
...
PS: Whatch out if you want to apply attributes like "foreground", they seem to get overwritten by set_entry_text_column(0) if set befor.
E.g: if items in the model look like:
["TEXT_YOU_WANT_TO_DISPLAY","TEXT_FOREGROUNDCOLOR_AS_MARKUP_COLOR"]
The change to the foregroundcolor can be applied with the following:
...
self.SelectionBox.add_attribute(self.cell, 'text', 0)
self.SelectionBox.set_entry_text_column(0)
self.SelectionBox.add_attribute(self.cell, 'foreground', 1)
self.SelectionBox.set_active(0)
...
Related
I'm using the Qt framework to build my graphical user interface. I use a QGridLayout to position my QWidgets neatly.
The GUI looks like this:
My application regularly adds new widgets to the GUI at runtime. These new widgets are usually not added at the end of the QLayout, but somewhere in the middle.
The procedure to do this is a bit cumbersome. Applied on the figure above, I would need to take out widg_C, widg_D, ... from the QGridLayout. Next, I add widg_xand widg_y, and finally I put the other widgets back again.
This is how I remove widgets from the QGridLayout:
for i in reversed(range(myGridLayout.count())):
self.itemAt(i).widget().setParent(None)
###
As long as you're dealing with a small amount of widgets, this procedure is not a disaster. But in my application I display a lot of small widgets - perhaps 50 or more! The application freezes a second while this procedure is ongoing, which is very annoying to the user.
Is there a way to insert widgets somewhere in a QLayout, without the need to take out other widgets?
EDIT: Apparently the solution for the QVBoxLayout is very simple. Just use the function insertWidget(..) instead of addWidget(..). The docs can be found a this link: http://doc.qt.io/qt-5/qboxlayout.html#insertWidget
Unfortunately, I couldn't find a similar function for the QGridLayout.
EDIT: Many people rightly mentioned that putting back a lot of widgets shouldn't cause a performance issue - it is very fast indeed (thank you #ekhumoro to point that out). Apparently, the performance issue I faced had to do with the algorithm putting the widgets back. It is a fairly complicated recursive algorithm that puts every widget on the right coordinates in the QGridLayout. This resulted in a "flicker" on my display. The widgets are taken out, and put back inside with some delay (due to the algorithm) - causing the flicker.
EDIT: I found a solution such that I can easily insert new rows into the QGridLayout. Inserting new rows means that I don't need to take out and replace all the widgets from scratch - hence I avoid the expensive recursive algorithm to run.
The solution can be found in my answer below.
Thank you #ekhumoro, #Stuart Fisher, #vahancho and #mbjoe for your help. I eventually found a way to solve the issue. I no longer use the QGridLayout(). Instead, I built a wrapper around the QVBoxLayout to behave as if it was a GridLayout, with an extra function to insert new rows:
class CustomGridLayout(QVBoxLayout):
def __init__(self):
super(CustomGridLayout, self).__init__()
self.setAlignment(Qt.AlignTop) # !!!
self.setSpacing(20)
def addWidget(self, widget, row, col):
# 1. How many horizontal layouts (rows) are present?
horLaysNr = self.count()
# 2. Add rows if necessary
if row < horLaysNr:
pass
else:
while row >= horLaysNr:
lyt = QHBoxLayout()
lyt.setAlignment(Qt.AlignLeft)
self.addLayout(lyt)
horLaysNr = self.count()
###
###
# 3. Insert the widget at specified column
self.itemAt(row).insertWidget(col, widget)
''''''
def insertRow(self, row):
lyt = QHBoxLayout()
lyt.setAlignment(Qt.AlignLeft)
self.insertLayout(row, lyt)
''''''
def deleteRow(self, row):
for j in reversed(range(self.itemAt(row).count())):
self.itemAt(row).itemAt(j).widget().setParent(None)
###
self.itemAt(row).setParent(None)
def clear(self):
for i in reversed(range(self.count())):
for j in reversed(range(self.itemAt(i).count())):
self.itemAt(i).itemAt(j).widget().setParent(None)
###
###
for i in reversed(range(self.count())):
self.itemAt(i).setParent(None)
###
''''''
I do not find a correct answer to my issue, despite intense research and a rather simple problem.
All I would like to do, is my comboboxes drop down when clicked on by 'Button-1'. But regardless of what I code, the combos don't behave as I wish.
following I prepared a simple code to demonstrate my problem:
from tkinter import *
import tkinter.ttk
def combo_events(evt):
if int(evt.type) is 4:
w = evt.widget
w.event_generate('<Down>')
root = Tk()
li = ('row 1', 'row 2', 'row 3')
combo1 = tkinter.ttk.Combobox(root, value=li)
combo2 = tkinter.ttk.Combobox(root, value=li)
combo1.bind('<Button-1>', combo_events)
combo2.bind('<Button-1>', combo_events)
combo1.pack()
combo2.pack()
root.mainloop()
Well, if I try this code, the combos do dropdown, but not as expected. So, I tried to add a bind of the 'FocusIn' event but that rather complicates the situation and inhibits a 'FocusOut' ...
Can any1 help me to achieve my goal?
ps: I know, that the combo will drop down by clicking on the frame of the widget, but to be more precise I would like to drop it, when clicking into it.
And by the way, where do I find a rather complete list of events a combobox can trigger?
thx for effort and answer.
Why are you using int(evt.type) is 4 instead of int(evt.type) == 4 ?
Applying this change it works for me.
Edit 1
First of all, thank you for explaining to us what you really want to have. Did not expect this from your initial question.
If you want to override the editing behaviour it is time to dig a little deeper.
The widget you click into inside the combobox is an entry widget. What you can do now is to define when your event shall be fetched inside the event chain. Will apply code soon.
Edit 2
To get it at the first mouse click:
w.event_generate('<Down>', when='head')
Why? Because default of Event Generate is to append the generated Event to the Event Chain (put it at its end, value = 'tail'). Changing to when='head' gives your desired behaviour.
I am beginning to work on a program in which i want multilingual support, but since it is pretty modular (and i want it to be even more in the future), a language change means "destroy what you had of interface and build again with the content which language modules have". (You can see the source as of now on GitHub)
This full-modular approach may give many problems, but i still want it, and so the problem is: Whenever i destroy the widgets i had, until i am alone with the raw Gtk.Window itself, i am not able to assign once again widgets to it. They won't get displayed at all, sometimes silently, sometimes with errors depending on my approach.
Lets suppose the class window, which inherits from a Gtk.Window.
This class is the raw window, and i assign to it a Gtk.Box -
self.interface.
self.interface itself, has two Gtk.Box's, one sidebar and one stack of contents.
To rebuild i tried to:
Change the language variable
Use the method destroy on self.interface, which removes the widget and its child's.
Reuse the function to build the widgets stack on top of self.interface
Re-add self.interface to self (window).
This approach returns:
g_object_unref: assertion 'G_IS_OBJECT (object)' failed
Gtk.main()
Pointing to the .show_all() method in this file.
I've already tried to leave interface without using .destroy on it, applying only on its child's, and then creating them again over it, but didn't worked. The window was left blank with no error at all.
The code i am trying right now is:
#Remember, self is a Gtk.Window()
def __init__(self):
[...]
self.interface = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.stack = None
self.add(interface)
self.build_interface()
def build_interface(self):
self.interface.pack_start(
self.create_side_navigation(
self.interface_data["menu"][self.language]["name"])
, False, False, 0
)
self.stack = self.create_content_stack(self.interface_data["menu"][self.language])
self.interface.pack_start(self.stack, True, True, 0)
###Code to rebuild(this is a question dialog):
if response == Gtk.ResponseType.OK:
self.language = self.new_language["Index"]
self.new_language = None
self.stack.destroy()
self.interface.destroy()
self.interface = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.build_interface()
self.add(self.interface)
This code will cause the previously told "g_object_unref" error.
What is causing this? Why can't i add anything once deleted?
Feel free to clone the repo and try, the code is well commented(yet i am not sure if poorly written, i am a python newbie) and its quite easy to understand where is the problematic part. Its not too big.
PS: It should need GTK+3.12 because of the popovers.
As a GTK Dev showed to me, GTK has by default all the widgets invisible.
The error was caused in the line which declared the whole interface visibility (windowclass.show_all()), but since the interface changed since when it was applied, it threw that warning.
He pointed me to .remove() instead of .destroy(), and to set .show_all() to the new elements after set up.
The next commit(or the following) on that git, has the solution.
The best way to be multilingual is to keep your widgets the same and merely change the text of labels and titles. This can be done without disturbing the overall setup. For example:
s='Stop'
if lang=='fr': s='Arret'
...
somelabel.set_label(s)
I'm trying to write a very basic Kivy program that will use 3 different layouts to split the screen into :
a header (at the top of the screen)
a text zone (in the middle of the screen)
a console (at the bottom of the screen)
So far I was thinking to use a main gridLayout, in which I use 3 different floatLayout.
Here's what the code looks like:
class Logo(App):
def build(self):
layout = GridLayout(rows=3)
layoutTop = FloatLayout(size=(100,300))
layoutMid = FloatLayout(size=(100,300))
layoutDown = FloatLayout(size=(100,300))
logo = Image(source='imagine.png',size_hint=(.25,.25),pos=(30,380))
blank = Label(text='', font_size = '25sp',pos=(-200,100))
titre = Label(text='#LeCubeMedia',font_size='40sp',pos=(0,280))
ip = Label(text='192.168.42.1',font_size='25sp',pos=(250,280))
layoutTop.add_widget(titre)
layoutTop.add_widget(logo)
layoutTop.add_widget(ip)
layoutMid.add_widget(blank)
layout.add_widget(layoutTop)
layout.add_widget(layoutMid)
return layout
if __name__ == '__main__':
Logo().run()
Actually my problem is regarding the creation of the console. I have read a lot of the Kivy docs, but I am still looking for a good way to do this type of widget.
How do you think it would be if I send something with a Python print into my Kivy app, and then refresh as soon as I need to send something else (to erase the previous print). This way it would be a console-like. But, so far I have not much ideas..
Any ideas ?
I have seen 2 types of consoles in Kivy. The first is a multiline textinput in a scrollview where you append the new text to the old in the textinput. The second is a BoxLayout or GridLayout in a Scrollview where each console output is a separate label in the layout.
This was a attempt trying stuff out with kivy, the code is old and you might need to adjust it a bit to make it run with latest kivy. Kivy-designer also includes this. This is using the simple way of using two textinputs, 1 for history and the other for input.
A better way to do a proper console would be to use pyte and draw characters directly onto the canvas of a widget. This way one would get VT emulation for free.
Image: http://dl.dropbox.com/u/4900888/GUI.png
Source and UI is in comments, due to "New-user-limiations"
I'm sorry if I've totally misunderstood what this site is about, but I have a problem.
When the button in the lower left corner is pressed, I want to get the values from the three spinboxes and save the as variables for use in a function that is triggered by the button.
I'm really blank as to how you would do this.
Any help is appreciated, and additional information can be supplied if it needs to.
EDIT:
I am using Python, and Gtk+ through Glade.
PS:
Does Stack Overflow have any code sharing site prefferences such as pastebin and so on?
The short answer is: with the spin button's get_value_as_int() method.
I suspect, however, that your actual problem is getting a reference to the spin button to call get_value_as_int() on. From your code I see that you are using gtk.Builder to build your UI.
Accessing widgets
Keeping a reference to specific widgets in Handler instance:
handler = Handler()
builder = Gtk.Builder()
...
# Store references to widgets in `handler`
for widget_name in ('sbtn_days', 'sbtn_hours', 'sbtn_minutes'):
setattr(handler, widget_name, builder.get_object(widget_name))
# The above is equivalent to the following:
handler.sbtn_days = builder.get_object('sbtn_days')
...
# In signal handling code:
days = self.sbtn_days.get_value_as_int()
Or you can keep a reference in to builder in your Handler instance:
builder = Gtk.Builder()
handler = Handler()
handler.builder = builder
...
# In signal handler code:
sbtn_days = self.builder.get_object('sbtn_days')
days = sbtn_days.get_value_as_int()
Notes
In the code above,
I assumed that your spin button for days is named sbtn_days. Adjust as necessary.
I only demonstrated accessing sbtn_days and its value. The other buttons can be accessed in a similar way.
P.S. There are a bunch of other problems with your code keeping it from being "good".