Daten aus JTable per Drag 'n Drop in ein JLabel verpacken und auf einem JPanel platzieren

#1

Moin,

ich versuche, dem Thread-Titel entsprechend, Daten, die ich in einer JTable zur Auswahl habe, per Drag 'n Drop in ein JPanel zu bugsieren.
Die JTable habe ich in einem javawx.swing.Popup-Objekt untergebracht, das sich ereignisgesteuert über der Oberfläche darstellt. Ein entsprechendes Beispiel konnte ich dazu auf Creating Popup Components finden.
Aus der JTable möchte ich nun durch Ziehen (drag) mit der Maus ein Objekt aus der JTable selektieren und dessen Daten mit einem JLabel visualisieren, das am Ende in einem JPanel in meiner GUI eingebaut werden soll (drop).

Bisher habe ich nur brauchbare Beispiele im Netz gefunden, die die Standardkomponenten abdecken, die den Mechanismus bereits implementiert haben. Oder Beispiele, die den entscheidenden Schritt des “Droppens” auf ein JPanel nicht dabei haben.

Kann mir jemand mit einem einfachen nachvollziehbaren KSKB auf die Sprünge helfen?
Im Moment experimentiere ich mit einem MouseListener an der JTable und einem DropListener am Panel. Mal sehen, wo das hinführt.

0 Likes

#2

Kürzlich hatte ich aus gegebenem Anlass mal ein Beispiel zusammengeschustert, wo aus einer JTreeTable ein String in ein JLabel geschoben wird: https://github.com/javagl/JTreeTable/blob/master/src/test/java/de/javagl/treetable/test/JTreeTableDragAndDropExample.java Vielleicht könnte einiges davon als “Inspiration” helfen.

So ganz ist mir das im Detail aber noch nicht klar:

Aus der JTable möchte ich nun durch Ziehen (drag) mit der Maus ein Objekt aus der JTable selektieren und dessen Daten mit einem JLabel visualisieren, das am Ende in einem JPanel in meiner GUI eingebaut werden soll (drop).

Im verlinkten Beispiel wird einfach die String-Repräsentation einer Zelle verschoben. Sollen bei dir mehrere Zellen verschoben werden können, und da vielleicht auch die spezifischen Daten, die in der Tabelle liegen? (Z.B.: In der Tabelle liegt ein Person-Objekt, von dem der getName mit einem passenden cell renderer angezeigt wird - da sollte dann wohl nicht der Name, sondern das Person-Objekt gezogen werden, oder?)

0 Likes

#3

Hallo Marco, Vielen Dank für deine Antwort. Ich freue mich immer, wenn ich Antworten von dir bekomme.:+1:

Genau!

In der JTable sind je Zeile Daten eines Bauteils einer Maschine gespeichert. Ich hatte gehofft, das ausgewählte Objekt aus der JTable in ein JLabel transferieren, um dort bspw. Name und Grafik, die im Bauteil-Objekt gespeichert sind, zu visualisieren. Dieses JLabel muss also noch vor dem Drop ins JPanel erzeugt werden. Ich möchte also Daten aus einer JTable in ein JPanel übertragen, mit einem JLabel als notwendige Zwischenstation.
Also sinngemäß: JTable -> Drag-Daten -> JLabel -> Drop-JPanel

Ich muss erst mal den Mechanismus verstehen, damit ich irgendwann das auch mal selbst implementieren kann.

0 Likes

#4

Wenn du sagst

In der JTable sind je Zeile Daten eines Bauteils einer Maschine gespeichert.

dann ist nicht ganz klar, wie der “Rückweg” aussehen kann. Wenn man auf eine Tabellenzelle klickt, kommt man dann noch ohne weiteres an das eigentliche Objekt dran, was dahintersteckt? Z.B. wenn man sowas hat wie (pseudocode)

List<Part> parts = ...;
for (int i=0; i<10; i++) {
    Part part = parts.get(i);
    tableModel.addRow(part.getName(), part.getID(), part.getSize());
}

Dann stünden in der Tabelle ja ggf. nur Strings und Integers, und man käme nur “indirekt” an die Part-Objekte ran - nämlich über den Index (Tabellenzeile - und da halt immer aufpassen, falls man einen Table-Sorter drin hat :wink: ).

Wenn man an die Part-Objekte drankommt, wäre es noch praktisch zu wissen, ob die Serializable sind. Soweit ich weiß kann man sich dann das erstellen einer eigenen Transferable-Implementierung sparen. Aber in sooo vielen Varianten hab’ ich DnD selbst noch nicht benutzt, da müßt ich auch nochmal reinschauen. (Ein eigenes Transferable ist nicht wirklich aufwändig, aber eine einfache(re) Lösung wäre ja vielleicht praktisch)

Dass das JLabel da so explizit auftaucht wundert mich etwas. Das ist ja vermutlich nicht “zwingend”, in dem Sinne, dass man ja vermutlich einfach eine Methode

class DropPanel extends JPanel {
    void acceptDroppedStuff(Part part) {

        // Might create a JLabel here.... 
    }
}

erstellen würde - ob da nun ein JLabel verwendet wird, wäre für die “Schnittstelle” dann ja egal. (Es könnte auch ein CloseablePanel sein :wink: )

0 Likes

#5

Hi Marco, danke für deine Antwort.

Die Sache ist eigentlich eine Einbahnstraße. Es sollen nur Daten aus der Tabelle ins Panel übernommen und dargestellt werden. Aus dem Panel soll nichts in die Tabelle geschoben werden.
Das TableModel sammelt in einer Liste die Bauteile einer Maschine. Dazu bekommt das Model eine zuvor zusammengebaute Liste übergeben, die aus Bauteil-Objekten besteht und in der JTable dargestellt werden.
Das Model ist so gebaut, dass es nicht nur die String-Repräsentationen der Daten ausgibt, es kann auch ein selektiertes Bauteil zurückgeben.

Am Ende soll dann das Bauteil, das aus der Tabelle gezogen wurde, im Panel symbolisch dargestellt werden. Ich wollte schon, dass das Panel das für die Darstellung benötigten JLabel erstellt. Da ich die JLabel auf dem Panel positionieren will, sollte das JLabel bereits nach dem Verlassen der Tabelle über dem Panel zu sehen sein.
Ich habe heute ins Oracle Tutorial zum DnD geguckt. Es hat aber erst Mal noch mehr Fragen aufgeworfen, als es beantwortet hat.

Schöne Grüße
Gernot

0 Likes

#6

Das mit dem “Rückweg” bezog sich darauf: In der JTable wird auf irgendeine Tabellenzelle gedrückt, um das DnD zu starten. Kommt man von der angeklickten Tabellenzelle noch auf das echte, dahinter liegende Part-Objekt?

So, wie es jetzt formuliert ist, klingt das ja so, als könnte es da eine Stelle geben, wo man alles beisammen hat (Pseudocode)

List<Part> parts = ...;
TableModel model = createModelFrom(parts);
JTable table = new JTable(model);

// Grob wie im verlinkten Beispiel:
DragGestureListener dragGestureListener = new DragGestureListener() {
    @Override
    public void dragGestureRecognized(DragGestureEvent dge) {
        int row = getSelectedRow(table);
            
        Part part = parts.get(row); // HIER hat man das relevante Part-Objekt!
        machDamitIrgendwas(part);
    }
}

(Es hätte ja sein können, dass man “irgendwo” eine JTable bekommt, und die Information, in welcher Zeile welches Part-Objekt steht, durch Zwischen- und Abstraktionsschritte verloren gegangen ist. Eigentlich muss die row noch von der View ins Modell konvertiert werden, falls die Tabelle z.B. sortiert sein kann…)

Aber falls das ungefähr so ist, kommt man an der “Quelle” schonmal ohne Probleme an das Part-Objekt.


Der “Empfänger-Teil” des Part-Objekts ist dann das Panel.

Dort soll dann, wenn ich das richtig verstanden habe, das JLabel angezeigt werden, wenn man droppt, aber auch schon während man das Bauteil nur über das Ziel-Panel drüberzieht.

Das klingt ein bißchen wie etwas, was ich auch schon mal gemacht habe. Aaaber: Das habe ich bisher nur einmal gemacht, und in einem recht “komplizierten” Zusammenhang, und ich habe mich da auch ein bißchen zum Ziel hingestolpert :flushed: D.h. vielleicht gibt es auch eine vieeel einfachere Lösung, die ich nur nicht gefunden habe…

Das ganze ist Teil von https://github.com/javagl/Flow :

gif

(Dass das so halbtransparent-grün ist, ist natürlich nur eye candy ;-))

Die relevantesten Teile sind hier wohl…

Das ist alles im Rahmen einer “komplexeren” Applikation, und es gibt sicher Konstellationen, wo das ganze “einfacher” gelöst werden kann. Bezogen auf das Mini-Beispiel: Dort ist eben nur die drop-Methode (ziemlich trivial) implementiert. Um das JLabel auch schon beim Drüberziehen anzuzeigen, müßte dort dragOver passend implementiert werden: https://github.com/javagl/JTreeTable/blob/master/src/test/java/de/javagl/treetable/test/JTreeTableDragAndDropExample.java#L151


Allgemein: DnD ist ziemlich kompliziert, das stimmt. Und alles, was ich hier sage, ist genau unter diesem Vorbehalt zu sehen: Vielleicht gibt es einfachere Lösungen.

Was ich bei DnD schwierig fand: Es gibt zwei verschiedene “Ebenen”, an denen man ansetzen kann: Bei manchen Sachen muss man nur wenige Zeilen Code schreiben, und sowas sagen wie

source.enableDrag(true);
new DropTarget(target);

und dann passiert die ganze Magie automatisch (das ist der “Drafult Support”, der unter https://docs.oracle.com/javase/tutorial/uiswing/dnd/defaultsupport.html beschrieben ist).

Aber sobald man auch nur einen Hauch weiter runter muss, muss man sich mit DragGestureListener, Transferable, DataFlavor & Co rumschlagen. So gesehen ist klar, dass das kompliziert ist, wenn man bedenkt, dass man ja auch komplexe Objekte in die Anwendung rein und aus der Anwendung raus draggen können will. Z.B. “Drag-And-Drop einer Datei aus dem Windows-Explorer in die Anwendung, um die Datei zu öffnen”. Aber es gibt einige Tutorials und Beispiele, an denen man sich entlanghangeln kann (auch wenn das Gefühl etwas unbehaglich ist, das ganze nicht in aller Tiefe “verstanden” zu haben…)

0 Likes

#7

Nebenbei: Wenn du ein KSKB mit einem Dummy-Part und einer einfachen JTable hast, schau’ ich da vielleicht mal.

0 Likes

#8

Hallo Marco, danke, dass du dir die Zeit genommen und dir die Mühe gemacht hast, mir bei der Lösung meines Problems zu helfen!

Ja, in etwas so, wie du das in deinem Pseudocode-Fragment skizziert hast.

Wow, die Animation trifft den Nagel auf den Kopf! Eine etwas angepasste Variante könnte schon die Lösung sein. Danke für die Links zu den Teilen deiner Projekte. Die schaue ich mir auf jeden Fall an. Ich gehe davon aus, das ich da einiges lernen werde. :+1:

Ja, darüber habe ich mir Gedanken gemacht. In einer ersten Testversion, die ich mal gebaut habe, wurden alle “gültigen” Bauteile am oberen Rand des Panels aufgereiht. Da war es möglich, die Bauteile in einen Bereich zu verschieben, den man mit der Maus nicht mehr “erreichen” konnte.
Mein Gedanke dazu war, einen Bereich (Rectangle) festzulegen, in dem die Bauteile nach dem Verschieben liegen müssen. Anderenfalls werden sie aus dem Panel entfernt, bzw. zur Auswahl der Positionierung (DnD) hinzugefügt.

Auch zu diesem Teil habe ich bereits eine Lösung vorbereitet. An den JLabels, in denen die Bauteile dargestellt werden sollen, ist ein MouseListener registriert, der anbietet, ein Bauteil per Rechtsklick-Kontextmenü u.a. aus dem Panel zu löschen. Nach dem Löschen wird das Bauteil wieder Auswahl der Positionierung (DnD) hinzugefügt.

Oh ja, wie sehr du mir da aus der Seele sprichst…

Habe ich bisher nicht, werde da aber wohl nicht herum kommen.

Danke erst mal bis hier her! Ich schaue mir erst mal die Code-Fragmente aus deinen Links an, vielleicht komme ich ja damit bereits weiter. Dafür brauche ich etwas Zeit. Ich melde mich bei Erfolg oder Misserfolg.

Schöne Grüße

Gernot

0 Likes

#9

Das sollte dann fokussierter möglich sein, indem man dragExit (neben dragOver und drop) passend implementiert.

Ja, da gibt es sicher verschiedene Optionen. Je nach Szenario ja vielleicht auch dedizierte Buttons “Remove last” oder “Clear”…

0 Likes