tkinter listbox drag and drop - drag-and-drop

I'm trying to make a listbox with the ability to do drag and drop onto a canvas. I've done drag and drop before, but it was only between canvas.create_text loosely based on the code for a this checkers program I found here. I've seen a couple of questions about drag and drop listboxes but they only deal with changing the order of elements in the listbox. What I'm dealing with is a listbox which has a list of names, and a canvas with some create_text objects on the canvas, and I want to be able to drag a name from the listbox onto the canvas. If figure I'd need to make a Listbox subclass, but I'm unsure of where to go from there.
So I've got a DialogWindow (subclass of Toplevel), and have my canvas and listbox in the DialogWindow. I've conjured up a way of getting a name from the listbox: when I click on a name, I convert it to a canvas.create_text object and then drag that. My issue is the drop. I try to use the canvas.canvasx to convert to canvas coordinates but it hasn't worked for me. x and y are still in listbox coordinates.
def onRelease(self, event):
x = self.canvas.canvasx(event.x)
y = self.canvas.canvasx(event.y)
print(event.x, event.y)
print(x, y) #Prints the same thing as the previous line

The key to drag and drop boils down to needing to do three things:
bind on <ButtonPress-1> to select the item to be dragged
bind on <B1-Motion> to do the drag
bind on <ButtonRelease-1> to do the drop
None of this requires any subclassing. All of these bindings are on the listbox widget. You'll probably want to create an instance of Toplevel with a text label in it and the window decorations removed (using wm_overrideredirect(True)) to represent the item being dragged.
On the drop, you'll need to convert the coordinates of the mouse to canvas coordinates using the canvasx and canvasy methods of the canvas. Instead of starting with event.x and event.y (which are relative to the listbox), use the winfo_pointerxy method to get the screen coordinates of the mouse, then do a little math.
Here's an example of how you could do the drop:
def _on_drag_drop(self, event):
i = self.listbox.curselection()[0]
text = self.listbox.get(i)
wx, wy = self.canvas.winfo_rootx(), self.canvas.winfo_rooty()
x,y = self.winfo_pointerxy()
cx = self.canvas.canvasx(x-wx)
cy = self.canvas.canvasy(y-wy)
self.canvas.create_text(cx,cy,text=text, anchor="sw")
self.canvas.configure(scrollregion=self.canvas.bbox("all"))

Related

enable context menu for specific cell or item in uitable or uilistbox in matlab

I created a uitable (new version using appdesigner) in MATLAB and wanted to support right clicking on cells and showing a cell specific context menu. Much to my surprise there seemed to be no way to support this.
The context menu only seems to trigger with right click on the uitable, but there is no way of knowing which cell was selected (I think, maybe not?). I created a workaround where I left clicked to select a cell, and during that selection I right clicked using a Java Mouse robot to trigger the context menu. This is super ugly but sort of works. Except, if you need to bring up the menu twice on the same cell. Apparently the cell selected callback only fires once for the cell, until a new cell is selected. I tried literally putting two tables in the same spot and upon selecting one toggling to the other, but the memory of cell selection is table specific, so this only worked for two clicks before both tables had been clicked on the same cell, and toggling visibility back to the first resulted in the cell selection callback not firing (since the cell had not changed) . I tried various approaches to try and deselect the cell (disable/enable, visibility change, data change, etc.), but the cell selection callback never changed.
I even tried having duplicate columns, where the goal was to hide a column, where normally columns 1 and 2 would be visible (column 3 out of view due to size), and then on clicking on column 2, column 2 would hide itself (0 width) and column 3 (an exact duplicate) would move into its place, thus seeming to the user like multi-clicking was supported. Unfortunately I can't set the column width to 0 -- or rather, setting it to 0 doesn't completely hide the column. Instead there seems to be some minimal width to the column and the whole thing looked awful.
I wanted to do something similar with a listbox (right click support), but again I couldn't figure out how to identify where I was right clicking. I eventually settled on left clicking on a listbox and using the mouse robot approach to right click to bring up the context menu. Unlike the uitable, it was fairly easy to clear the selection on the listbox (set listbox.Value = {}). However, I strongly dislike the left click instead of right click approach and I'd rather have multiple columns.
Any suggestions would be much appreciated!!!
So I found an approach that is better than using a robot. I had tried this but was missing a critical portion which I will describe below.
Upon selecting a row in the table, the open command can be used to launch a context menu. My problem was that I didn't know where to launch the menu. I tried CurrentPoint for the figure, but it was 0,0 (or in general not valid)
Here's the current documentation for CurrentPoint:
Current point, returned as a two-element vector. The vector contains
the (x, y) coordinates of the mouse pointer, measured from the
lower-left corner of the figure. The values are in units specified by
the Units property.
The coordinates update when you do any of the following:
Press the mouse button within the figure.
Release the mouse button after pressing it within the figure.
Press the mouse button within the figure, and then release it outside
the figure.
Rotate the scroll wheel within the figure.
Move the mouse within the figure (without pressing any buttons),
provided that the WindowButtonMotionFcn property is not empty.
If the figure has a callback that responds to mouse interactions, and
you trigger that callback faster than the system can execute the code,
the coordinates might not reflect the actual location of the pointer.
Instead, they are the location when the callback began execution.
If you use the CurrentPoint property to plot points, the coordinate
values might contain rounding error.
Here's the critical line again:
"Move the mouse within the figure (without pressing any buttons), provided that the WindowButtonMotionFcn property is not empty."
So when a selection of a cell happens, the CurrentPoint is not valid. However, if we simply define a WindowButtonMotionFcn, then it is!
So the general idea is to have a callback for the table when a cell is selected (SelectionChangedFcn) and to set a dummy callback for WindowButtonMotionFcn
The final point is that a context menu can be launched with the open function if you specify a given location to launch it at. This is different from attaching it to an object and having it automatically launch on right click.
Here's some example code. If you comment out the callback for windows motion then the whole thing doesn't work! Unfortunately it is a left click for targeting the cell but at least it avoids the non-sense I was using with a java robot right click.
classdef wtf < handle
properties
h %struct, this was an appdesigner handle
cm %context menu
end
methods
function obj = wtf()
h = struct;
h.UIFigure = uifigure();
h.UITable = uitable(h.UIFigure);
obj.h = h;
obj.h.UITable.CellSelectionCallback = #obj.tableCall;
%obj.h.UITable.SelectionChangedFcn = #obj.tableCall;
%Some data ...
s = struct;
s.a = (1:4)';
s.b = (5:8)';
obj.h.UITable.Data = struct2table(s);
%Our context menu
cm = uicontextmenu(obj.h.UIFigure);
m = uimenu(cm,'Text','Menu1');
obj.cm = cm;
%WTF ... without this we don't get a valid CurrentPoint
obj.h.UIFigure.WindowButtonMotionFcn = #obj.mouseMove;
end
function tableCall(obj,x,y)
%y - event info
%x - impacted object
cp = get (obj.h.UIFigure, 'CurrentPoint');
open(obj.cm,cp(1),cp(2));
selected_cell = y.Indices;
%selected_cell = y.Selection;
x.Selection = []; %allows reselecting same cell without
%needing to select another cell first
%Now we can run something on the context menu
%that targets the selected cell
end
function mouseMove(obj,x,y)
%we could store a point here
end
end
end

unity instantiating multiple objects on the same position. I would like to spread them out

I am trying to spread out the instantiated objects to fill up the grey area in a grid layout. So far I am stuck at this point where I have all the objects stacked on eachother. I did try a few things but they would make the object not visible. Here is the code I have and two pictures. One of the scene and one of the hierarchy. scene picture hierarchy picture
public class stockSpawner : MonoBehaviour
{
[SerializeField] GameObject stockPrefab;
int x;
void Start()
{
x = Mathf.RoundToInt(scene2Calc.nS);
}
void Update()
{
if (x >= 1)
{
Instantiate(stockPrefab, transform);
x--;
}
else { Debug.Log(x + "this is x value");}
}
}
It seems all of your assets are UI. If you want to create a grid, normally I would say to instantiate the objects and change the transform at which they spawn in a double loop. In one loop you would iterate the horizontal axis of spawning while the other moves the vertical. However, as your setup is all UI, you can use some nifty components to do all the heavy lifting for you.
Attach a grid layout group to the parent object of where you want to spawn all of these UI objects. I would recommend attaching this component to the Panel in your screenshot. I would also recommend changing your installation code slightly as you are working with UI objects. To assure anchoring, rescaling, etc. work, you will want to use Instantiate(stockPrefab, transform, false);. That last parameter is instantiateInWorldSpace, which from the docs,
When you assign a parent Object, pass true to position the new object
directly in world space. Pass false to set the Object’s position
relative to its new parent..
Now getting back to the main portion of your question. If you added the grid layout component to your panel, the objects will now be aligned into a grid. There are various different fields on this component you can change. They are on the docs, but I will also list them here for clarity.
Padding The padding inside the edges of the layout group.
Cell Size The size to use for each layout element in the group.
Spacing The spacing between the layout elements.
Start Corner The corner where the first element is located.
Start Axis Which primary axis to place elements along. Horizontal will fill an entire row before a new row is started. Vertical will fill an entire column before a new column is started.
Child Alignment The alignment to use for the layout elements if they don't fill out all the available space.
Constraint Constraint the grid to a fixed number of rows or columns to aid the auto layout system.
If you properly fill out all of these fields, your UI objects when spawned and childed to the Panel object that has this grid layout component will now not be on top of each other, but will form a grid.

Getting Mouse Points using Matlab App Designer

I realize that App designer does not support interactive figure manipulation, but I am wondering if I can open a separate figure window (not a UI window) with my graphic displayed on it so that I can still get the location of my mouse clicks. Currently the code below displays the figure on my GUI, and then opens another blank figure that records my mouse clicks. This is fine, but I need to also display the figure in the new window as well, and am having trouble doing so.
first frame = vid(:,:,:,1);
imshow(firstframe,'Parent',app.UIAxes);
[centers_X centers_Y]=getpts;
What worked for me was setting a callback on the image rather than the axes:
ih = imshow(firstframe,'Parent',app.UIAxes);
ih.ButtonDownFcn = {#im_ButtonDownFcn, app}; %app will be passed to the callback
Then in a separate file in the same folder (or as a private function within the appdesigner... it should work but I haven't tried it):
function im_ButtonDownFcn(im, hit, app)
mouse_pos = flip(hit.IntersectionPoint(1:2)); %gives floats. Round if you want integers e.g. for indexing pixels

core-drag-drop and core-list: controlling x & y coords of avatar

I've created a core-list which supports ordering items by drag and drop using core-drag-drop and it works.
However, what I want is to force the x coordinate of the dragging item to always be pinned to the left edge of the list. Similarly I don't want to allow the items to be dragged off the top or bottom of the list.
I can't figure out a way to do that with core-drag-drop as it seems hardwired to move the top left of the dragging item to the mouse coords.
Any suggestions?

Select area by dragging mouse in gwt

I am using gwt with gwt-dnd and I want to do the following:
1. Select rectangle area by dragging the mouse
2. Select all the elements that are in this area
3. drag all selected elements.
Is there any idea?
On MouseDownEvent record the coordinates of the pointer (event.getClientX() and eventGetClientY()).
On MouseUpEvent do the same. If coordinates are different, you have a selected rectangular.
Get the widget which contains all the widgets or elements that are selectable. Loop through its children.
Compare coordinates of each widget with your rectangular (use getAbsoluteTop(), getAbdoluteLeft(), getOffsetHeight(), and getOffsetWidth()). Select widgets that are totally or partially inside the selected area.
I would add to Andrei's answer that if you to provide feedback by displaying the rectangle during selection that what we do is display the rectangle as an instance of com.google.gwt.user.client.ui.HTML with a style that displays the borders. This is updated using onMouseMove using setPixelSize and setWidgetPosition.
The library gwtquery-plugins offers a MultiSelect feature, so I will give it a try.
gwtquery-plugins