How to remove ComboBox selection using button (Julia Gtk)? - button

I am trying to do a simple GUI using Gtk with the Julia programming language, however, when I try to get the button to remove the active selection in the combobox programmatically, I get an "AssertionError".
"ERROR: AssertionError: xor(prev, current_task() !== g_stack)"
I am not sure how to get this simple example to work ?
Can anyone point me in the right direction ?
Here is my non-functional code:
using Gtk
# Create widgets------------------------------------
cb = GtkComboBoxText()
button = GtkButton("Remove Active")
# Create and Add choices to ComboBox ---------------
choices = ["zero", "one", "two", "three", "four"]
for choice in choices
push!(cb,choice)
end
# Function to get the selected choice (text) from the ComboBox
function getChoice()
i = get_gtk_property(cb, "active", Int)
return choices[i+1]
end
# Function that handles the ComboBox selection change---
function selection_changed(widget)
sel = getChoice()
println("We selected: $sel")
end
# Function to handle the button press------------------
function removeChoice(widget)
set_gtk_property!(cb,:active,-1)
end
# Connect the signals to the widgets -------------------
signal_connect(selection_changed, cb, "changed")
signal_connect(removeChoice, button, "clicked")
# Create window, and add widgets to it using Box Layout
win = GtkWindow("ComboBoxText Example",200,50)
vbox = GtkBox(:v)
push!(win, vbox)
push!(vbox, cb)
push!(vbox, button)
showall(win)

Note the warning at the end of this Gtk.jl manual page:
Warning: it is essential to avoid task switching inside Gtk callbacks, as this corrupts the Gtk C-stack. For example, use #async print or queue a message for yourself. ...
if you are still seeing segfaults in some random method due to there existing a callback that recursively calls the glib main loop (such as making a dialog box) or otherwise causes g_yield to be called, wrap the faulting code in GLib.#sigatom. This will postpone execution of that code block until it can be run on the proper stack (but will otherwise acts like normal control flow).
This is what happens here, when you try to change the selection status of the combo box from with a signal-handler callback - the "callback that recursively calls the glib main loop" as the manual page calls it.
Using either #async or Gtk.GLib.#sigatom in front of the set_gtk_property! call avoids this problem and allows the code to run.
In this case, that leads to a different error message because removeChoice itself leads to selection_change being called, and the getChoice call made there does not take into account that get_gtk_property(cb, "active", Int) could return -1. So we get a BoundsError. How you fix that depends on your use case, for demo purposes I just return nothing here in that case:
# Function to get the selected choice (text) from the ComboBox
function getChoice()
i = get_gtk_property(cb, "active", Int)
return i >= 0 ? choices[i+1] : nothing
end
# Function that handles the ComboBox selection change---
function selection_changed(widget)
sel = getChoice()
println("We selected: $sel")
end
# Function to handle the button press------------------
function removeChoice(widget)
#async set_gtk_property!(cb,:active,-1)
end
Running this, the output as I select two, then "Remove Active", then four, then "Remove Active" again in the GUI is:
julia> We selected: two
We selected: nothing
We selected: four
We selected: nothing

Related

Call functions within event handler in Python Turtle

I am trying to call the startagain function within the onclick event handler. The function does execute the input statement but does not allow the user to enter a response, the program just terminates.
import turtle
wn = turtle.Screen()
#Event Handler
def clicked(x,y):
print(x,y)
startagain()
#Start over
def startagain():
again = input("Want to start over? ")
if again == "y":
print("okay")
#The event
wn.listen()
wn.onclick(clicked)
wn.mainloop()
I'm not sure if I need to somehow stop the screen from listening or if there is another approach to this. Any help would be appreciated.
Your code works for me as-is. However, I would make two changes. First, I would have clicked() call startagain() indirectly via ontimer() (with no time delay), so that we don't end up with multiple clicked() calls on our stack. I would also obtain input via textinput() rather than switch back to the console for input:
from turtle import Screen, Turtle
# Event Handler
def clicked(x, y):
screen.ontimer(startagain)
# Start over
def startagain():
again = screen.textinput("Game Over", "Want to start over? ")
if again and again.lower().startswith("y"):
print("okay")
# The event
screen = Screen()
screen.onclick(clicked)
screen.mainloop()

How to use three different tkinter buttons with one command

Please house help. When using tkinter, I find it difficult to call a defined function in a button when the def is below the button. I use python 3.6.9. Example
import tkinter
window = tkinter.Tk
button = tkinter.Button(window, text="hello",command=newpage()).grid(column=0, row=0)
def newpage():
new = tkinter.toplevel()
The button does not work except I use lambda and also the lambda does not work if I define something new under the button. The new definition blocks the lambda from seeing the other def.
NB: I use the lambda like this lambda:newpage()
NB: I use python 3.6.9
Also please how can I make many tkinter buttons to use the same command (as in def)
The newpage function should really be defined before being used, just as a good programming practice if for no other reason.
The variable window is being set to tkinter.Tk instead of the object returned by tkinter.Tk().
The button is calling the function immediately because command expects a function to run, and thinks you want it to run something returned by newpage, instead remove the () command=newpage.
Toplevel is capitalized, tkinter.Toplevel().
A function can be used by any button, just assign the command to use the function:
import tkinter
def newpage():
new = tkinter.Toplevel()
window = tkinter.Tk()
button1 = tkinter.Button(window,
text="hello1",
command=newpage).grid(column=0, row=0)
button2 = tkinter.Button(window,
text="hello2",
command=newpage).grid(column=0, row=1)
button3 = tkinter.Button(window,
text="hello3",
command=newpage).grid(column=0, row=2)

When dragging multiple items from QListWidget, non-draggable items get removed

I have two QListWidgets. The user can select multiple items from one list and drag them to the other list. But within each list, some items are draggable and some are not. If the selection contains both draggable and non-draggable items, a problem happens. Only the draggable items appear in the second list, which is correct. But all the items disappear from the first list.
In the animated image above, items 00, 01, and 02 are selected. Only items 00 and 02 are drag enabled. After the drag-and-drop, all three items are gone from the first list. How can I fix this?
Here is some code to reproduce the problem:
import random
import sys
from PySide import QtCore, QtGui
class TestMultiDragDrop(QtGui.QMainWindow):
def __init__(self, parent=None):
super(TestMultiDragDrop, self).__init__(parent)
centralWidget = QtGui.QWidget()
self.setCentralWidget(centralWidget)
layout = QtGui.QHBoxLayout(centralWidget)
self.list1 = QtGui.QListWidget()
self.list1.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.list1.setDefaultDropAction(QtCore.Qt.MoveAction)
self.list1.setSelectionMode(QtGui.QListWidget.ExtendedSelection)
self.list2 = QtGui.QListWidget()
self.list2.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.list2.setDefaultDropAction(QtCore.Qt.MoveAction)
self.list2.setSelectionMode(QtGui.QListWidget.ExtendedSelection)
layout.addWidget(self.list1)
layout.addWidget(self.list2)
self.fillListWidget(self.list1, 8, 'someItem')
self.fillListWidget(self.list2, 4, 'anotherItem')
def fillListWidget(self, listWidget, numItems, txt):
for i in range(numItems):
item = QtGui.QListWidgetItem()
newTxt = '{0}{1:02d}'.format(txt, i)
if random.randint(0, 1):
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
else:
# If the item is draggable, indicate it with a *
newTxt += ' *'
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsDragEnabled)
item.setText(newTxt)
listWidget.addItem(item)
def openMultiDragDrop():
global multiDragDropUI
try:
multiDragDropUI.close()
except:
pass
multiDragDropUI = TestMultiDragDrop()
multiDragDropUI.setAttribute(QtCore.Qt.WA_DeleteOnClose)
multiDragDropUI.show()
return multiDragDropUI
if __name__ == '__main__':
app = QtGui.QApplication([])
openMultiDragDrop()
sys.exit(app.exec_())
Here I have some suspicion on setDefaultDropAction(QtCore.Qt.MoveAction)
Read below para from documentation: Specially the bold line
In the simplest case, the target of a drag and drop action receives a copy of the data being dragged, and the source decides whether to delete the original. This is described by the CopyAction action. The target may also choose to handle other actions, specifically the MoveAction and LinkAction actions. If the source calls QDrag::exec(), and it returns MoveAction, the source is responsible for deleting any original data if it chooses to do so. The QMimeData and QDrag objects created by the source widget should not be deleted - they will be destroyed by Qt.
(http://doc.qt.io/qt-4.8/dnd.html#overriding-proposed-actions)
First give a try with QtCore.Qt.CopyAction
Second, if MoveAction is mandatory, try creating QMimeData and QDrag objects in your source list widget's mouseMoveEvent.
Here in below link, you can find some help for creating QMimeData and QDrag objects in your source list widget's mouseMoveEvent. (code is in C++, My intention is to get conceptual idea).
http://doc.qt.io/qt-4.8/dnd.html#overriding-proposed-actions
I think Kuba Ober is right that this is a Qt bug. In the C++ source code, there is a function void QAbstractItemViewPrivate::clearOrRemove(). It deletes all selected rows, but it does not look at whether each item is drag-enabled or not.
That being the case, I came up with a few workarounds:
Method 1: Make all non-draggable items non-selectable as well
This is the easiest method. Just remove the QtCore.Qt.ItemIsEnabled flag from all non-draggable items. Of course if you want all of your items to be selectable, this won't work.
Method 2: Recreate the "startDrag" function
Since the clearOrRemove function belongs to a private class, I cannot override it. But that function is called by the startDrag function, which can be overridden. So I essentially duplicated the function in Python and replaced the call to clearOrRemove with my own function removeSelectedDraggableItems.
The problem with this method is that startDrag contains calls to a few other functions belonging to a private class. And those functions call other private class functions. Specifically, these functions are responsible for controlling how the items are drawn during the drag event. Since I didn't want to recreate all the functions, I just ignored those. The result is that this method results in the correct functionality, but it loses the graphical indication of which items are being dragged.
class DragListWidget(QtGui.QListWidget):
def __init__(self):
super(DragListWidget, self).__init__()
def startDrag(self, supportedDragActions):
indexes = self.getSelectedDraggableIndexes()
if not indexes:
return
mimeData = self.model().mimeData(indexes)
if not mimeData:
return
drag = QtGui.QDrag(self)
rect = QtCore.QRect()
# "renderToPixmap" is from a private class in the C++ code, so I can't use it.
#pixmap = renderToPixmap(indexes, rect)
#drag.setPixmap(pixmap)
drag.setMimeData(mimeData)
# "pressedPosition" is from a private class in the C++ code, so I can't use it.
#drag.setHotSpot(pressedPostion() - rect.topLeft())
defaultDropAction = self.defaultDropAction()
dropAction = QtCore.Qt.IgnoreAction
if ((defaultDropAction != QtCore.Qt.IgnoreAction) and
(supportedDragActions & defaultDropAction)):
dropAction = defaultDropAction
elif ((supportedDragActions & QtCore.Qt.CopyAction) and
(self.dragDropMode() != self.InternalMove)):
dropAction = QtCore.Qt.CopyAction
dragResult = drag.exec_(supportedDragActions, dropAction)
if dragResult == QtCore.Qt.MoveAction:
self.removeSelectedDraggableItems()
def getSelectedDraggableIndexes(self):
""" Get a list of indexes for selected items that are drag-enabled. """
indexes = []
for index in self.selectedIndexes():
item = self.itemFromIndex(index)
if item.flags() & QtCore.Qt.ItemIsDragEnabled:
indexes.append(index)
return indexes
def removeSelectedDraggableItems(self):
selectedDraggableIndexes = self.getSelectedDraggableIndexes()
# Use persistent indices so we don't lose track of the correct rows as
# we are deleting things.
root = self.rootIndex()
model = self.model()
persistentIndices = [QtCore.QPersistentModelIndex(i) for i in selectedDraggableIndexes]
for pIndex in persistentIndices:
model.removeRows(pIndex.row(), 1, root)
Method 3: Hack "startDrag"
This method changes the drop action from "MoveAction" to "CopyAction" before calling the built-in "startDrag" method. Then it calls a custom function for deleting the selected drag-enabled items. This solves the problem of losing the graphical dragging animation.
This is a pretty easy hack, but it comes with its own problem. Say the user installs an event filter that changes the drop action from "MoveAction" to "IgnoreAction" in certain cases. This hack code doesn't get the updated value. It will still delete the items as though the action is "MoveAction". (Method 2 does not have this problem.) There are workarounds for this problem, but I won't go into them here.
class DragListWidget2(QtGui.QListWidget):
def startDrag(self, supportedDragActions):
dropAction = self.defaultDropAction()
if dropAction == QtCore.Qt.MoveAction:
self.setDefaultDropAction(QtCore.Qt.CopyAction)
super(DragListWidget2, self).startDrag(supportedDragActions)
if dropAction == QtCore.Qt.MoveAction:
self.setDefaultDropAction(dropAction)
self.removeSelectedDraggableItems()
def removeSelectedDraggableItems(self):
# Same code from Method 2. Removed here for brevity.
pass

Scilab: How to use cbmenu in xclick()

I do not understand the explanation of the output argument cbmenu in the Scilab documentation of xclick.
It says:
cbmenu: String: callback associated to a menu if xclick returns due to a click on a menu.
I did not find any example in the web so I ask here. I have coded a snippet which lumps together the elements which may be relevant for cbmenu. The snippet does nothing, when I click on CLICK. Could anyone alter/complement/revise the code so that it does something - whatever it is to give me a clue what could be done with cbmenu?
xdel()
x=[-1 +1];
cf=figure(0);
plot(x,x)
m=uimenu(cf,'label','CLICK','callback','two=1+1');
[ibutton,xcoord,yxcoord,iwin,cbmenu]=xclick();
Kind regards
Rosestock
This is a minimal script which explains what the 5th output argument of xclick() can do:
xdel()
x=[-1 +1];
winnum=1; win=string(winnum);
cf=figure(winnum);
plot(x,x)
C=["Green" "Red" "Abort"];//case name strings
addmenu(winnum, C(1)); C1="execstr("+C(1)+"_"+win+"(1))";
addmenu(winnum, C(2)); C2="execstr("+C(2)+"_"+win+"(1))";
addmenu(winnum, C(3)); C3="execstr("+C(3)+"_"+win+"(1))";
while %t
[!,!,!,!,cbmenu]=xclick();
//Get the clicked option by cbmenu
if cbmenu==C1, cf.background=3; end
if cbmenu==C2, cf.background=5; end
if cbmenu==C3, break, end
end
If I get your intentions right, you want to create a custom menu. Here is a small demo, how to bulid custom menu elements, and also how to use locate to get back coordinates (but you don't need xclick to sense menu clicking). This code does not use explicitly cbmenu, but it is not necessary to get it work. If you run the code, first click the "Select range" menu, then click two times on the graph. Then the "Regression" menu is activated, and you can choose "Linear regession". By clicking "Exit program", the execution aborts. Not all nenu functions are implemented, they are there only to demonstrate a multilayer structure. I hope it helps!
//Interactive menu demo
// Scilab 5.5.2
// 2016.12.20. Attila Eredics
//
//Attention!
//Do not close the graphic window with the original x symbol
// while the program is running,
// because the program will run infinitely!
// But you can stop it in the Consol with: Ctrl+C and then typing: abort
clc;
clear;
lines(0);
//artificial data vectors
row = 100;
t=gsort(rand(row,1),'r','i'); //time
c=10*t+8; //concentration
c=c+rand(c); //add some noise
//graphic window
scf(0); clf(0);
w=gcf(); //get current figure
//new menu elements
m3=uimenu(w,"Label","Select range","Callback","m=3");
m4=uimenu(w,"Label","Regression","Enable","off","ForegroundColor","0.5|0.5|0.5");
m41=uimenu(m4,"Label","Linear","Callback","m=41","Enable","on");
m42=uimenu(m4,"Label","Nonlinear","Callback","m=42","Enable","on");
m5=uimenu(w,"Label","Save results","Callback","m=5");
m6=uimenu(w,"Label","Exit program","Callback","abort","ForegroundColor","1|0|0");
plot2d(t,c,style=-5,frameflag=6); //plot the data
xtitle("","time","concentration"); //axis labels
while %T //infinite loop
//-------------wait for user action (click on a menu)-------
m=0;
while m<1
xpause(1e4); //pause 10ms
end
//------------------the menu actions-------------------------
if m==3 then //Select range
mprintf("\nChoose 2 points (click on the graph)!");
coord=locate(2); //locate two clicks
disp(coord); //coord: according to the axes
//Select range in t vector
for i=1:row-1
if coord(1,1)>=t(i) & coord(1,1)<t(i+1) then //t(i) <= 1st click < t(i+1)
i1=i;
end
if coord(1,2)>=t(i) & coord(1,2)<t(i+1) then //t(i) <= 2nd click < t(i+1)
i2=i;
end
end
//selected range
clear tt;
clear cc;
tt=t(i1:i2);
cc=c(i1:i2);
plot2d(tt,cc,style=-5); //plot selected points
h2=gce();
h2.children.mark_foreground=3; //green
m4.Enable="on"; //enable Regression menu
m4.ForegroundColor="0|0|0"
m3.Enable="off"; //Disable selection menu
m3.ForegroundColor="0.5|0.5|0.5"
elseif m==41 then //linear regression of the selected points
mprintf("\nLinear regression");
[a,b,sig]=reglin(tt',cc');
plot2d([tt(1),tt($)],a*[tt(1),tt($)]+b,style=2); //blue line
m41.Enable="off"; //disable Linear regression menu
m41.ForegroundColor="0.5|0.5|0.5"
elseif m==42 then //Nonlinear regression
mprintf("\nNot implemented yet!");
elseif m==5 then //Save
mprintf("\nNot implemented yet!");
end
end

are there any events related to awful.prompt in Awesome WM 3.5?

I would like to know if it is possible to catch events from an awful.prompt widget like an event when the widget is activated with:
myprompt:run()
or when the user press Enter to validate his entry or Esc to leave/quit this widget.
There isn't a way to directly connect a signal on an awful.widget.prompt, but it is possible to specify some instructions to the prompt widget when the command has been executed:
in the awful/widget/prompt.lua the run function launch awful.prompt.run():
local function run(promptbox)
return prompt.run({ prompt = promptbox.prompt },
promptbox.widget,
function (...)
local result = util.spawn(...)
if type(result) == "string" then
promptbox.widget:set_text(result)
end
end,
completion.shell,
util.getdir("cache") .. "/history")
end
with some parameters which are :
args A table with optional arguments: fg_cursor, bg_cursor, ul_cursor, prompt, text, selectall, font, autoexec.
textbox The textbox to use for the prompt.
exe_callback The callback function to call with command as argument when finished.
completion_callback The callback function to call to get completion.
history_path Optional parameter: file path where the history should be saved, set nil to disable history
history_max Optional parameter: set the maximum entries in history file, 50 by default
done_callback Optional parameter: the callback function to always call without arguments, regardless of whether the prompt was cancelled.
changed_callback Optional parameter: the callback function to call with command as argument when a command was changed.
keypressed_callback
So I just have to use awful.prompt.run on my prompt box and specify the done_callback
Example: a prompt box in a wibox. The wibox is shown when the Mod4 + r keys are pressed, the wibox is hidden when the command is executed:
awful.key({ modkey }, "r", function ()
--promptlist is a table that contains wibox for each screen
if promptlist[mouse.screen].visible == false then
promptlist[mouse.screen].visible=true
awful.prompt.run({
prompt = promptlist.prompt[mouse.screen].prompt },
promptlist.prompt[mouse.screen].widget,
function (...)
local result = awful.util.spawn(...)
if type(result) == "string" then
promptlist.prompt[mouse.screen].widget:set_text(result)
--promptlist.prompt table that contains prompt widget for each screen
end
end,
awful.completion.shell,
awful.util.getdir("cache") .. "/history",
50,
function()
promptlist[mouse.screen].visible = false
end
)
else
promptlist[mouse.screen].visible=false
end
end),

Resources