Context menu (Copy / Cut / Paste)

 Menu konteks (Salin / Potong / Tempel) Pada Aplikasi Dengan Tkinter


Fitur ini ditambahkan unruk fungsional keyped HP agar dapat copy dan Paste teks dari luar ke fungsi fitur aplikasi ini.

Karena kita sedang membuat aplikasi dengan Tkinter, secara default widget Entry dan Text tidak otomatis punya binding untuk copy/paste di HP Android (misalnya lewat Pydroid3).

Tapi kita bisa patch dengan menambahkan push keyboard binding (<Control-c>, <Control-v>, <Control-x>) dan juga binding melalui tombol kanan (menu konteks) agar lebih nyaman.


🔧 Salin Patch – Tempel untuk Tkinter (HP / Desktop)

Kita bikin helper agar semua widget Entrydan Textpunya copy/paste:

def add_copy_paste(widget):
    """Aktifkan fungsi copy/paste di Entry atau Text widget"""

    def copy(event=None):
        try:
            widget.event_generate("<<Copy>>")
        except:
            pass
        return "break"

    def cut(event=None):
        try:
            widget.event_generate("<<Cut>>")
        except:
            pass
        return "break"

    def paste(event=None):
        try:
            widget.event_generate("<<Paste>>")
        except:
            pass
        return "break"

    # Bind ke keyboard
    widget.bind("<Control-c>", copy)
    widget.bind("<Control-C>", copy)
    widget.bind("<Control-x>", cut)
    widget.bind("<Control-X>", cut)
    widget.bind("<Control-v>", paste)
    widget.bind("<Control-V>", paste)

    # Tambah context menu (long press / klik kanan)
    menu = tk.Menu(widget, tearoff=0)
    menu.add_command(label="Copy", command=lambda: copy())
    menu.add_command(label="Cut", command=lambda: cut())
    menu.add_command(label="Paste", command=lambda: paste())

    def show_menu(event):
        menu.tk_popup(event.x_root, event.y_root)

    widget.bind("<Button-3>", show_menu)  # klik kanan
    widget.bind("<Button-2>", show_menu)  # long-press (kadang di HP masuk event ini)

🔧 Integrasi kesub_menu_modifikasi_resep_asal_ke_mmag.py

Setelah bikin Entrydan Textlangsung telpon add_copy_paste(widget):

# Nama Resep
nama_resep_entry = tk.Entry(input_frame, width=50)
nama_resep_entry.grid(row=0, column=1, pady=5, padx=5)
add_copy_paste(nama_resep_entry)

# Bahan
bahan_text = tk.Text(input_frame, width=60, height=6)
bahan_text.grid(row=1, column=1, pady=5, padx=5)
add_copy_paste(bahan_text)

# Cara Memasak
cara_text = tk.Text(input_frame, width=60, height=6)
cara_text.grid(row=2, column=1, pady=5, padx=5)
add_copy_paste(cara_text)

# Hasil Modifikasi
hasil_text = tk.Text(hasil_frame, width=80, height=18, state="disabled")
hasil_text.pack(fill="both", expand=True)
add_copy_paste(hasil_text)

✅ Hasil:

  1. Pintasan keyboard ( CTRL+C, CTRL+V, CTRL+X) aktif di Entri/Teks.
  2. Menu konteks (Salin / Potong / Tempel) muncul saat tekan lama (HP) atau klik kanan (Desktop) .
  3. Pengguna bisa dengan mudah menyalin bahan dari luar → tempel ke aplikasi .

Berikut kode untuk fitur konteks menu (Salin / Potong / Tempel) 

# =============== PATCH SALIN/TEMPAL ===============

def tambahkan_salin_tempel(widget):

    """Aktifkan Ctrl+C/V/X dan menu popup Copy/Paste di Text/Entry."""

    menu = tk.Menu(widget, sobek=0)

    menu.add_command(label="Potong", perintah=lambda: widget.event_generate("<<Potong>>"))

    menu.add_command(label="Salin", perintah=lambda: widget.event_generate("<<Salin>>"))

    menu.add_command(label="Tempel", perintah=lambda: widget.event_generate("<<Tempel>>"))

    menu.tambahkan_pemisah()

    menu.add_command(label="Pilih Semua", perintah=lambda: widget.event_generate("<<PilihSemua>>"))


    def popup(peristiwa):

        mencoba:

            menu.tk_popup(akar_peristiwa.x, akar_peristiwa.y)

        Akhirnya:

            menu.grab_release()


    widget.bind("<Button-3>", popup) # klik kanan / ketuk lama

    widget.bind("<Control-c>", lambda e: widget.event_generate("<<Salin>>"))

    widget.bind("<Control-v>", lambda e: widget.event_generate("<<Tempel>>"))

    widget.bind("<Control-x>", lambda e: widget.event_generate("<<Cut>>"))

    widget.bind("<Control-a>", lambda e: widget.event_generate("<<SelectAll>>"))


Atau kode yang ini:

def add_entry_context_menu(widget_entri):
    menu = tk.Menu(widget_entri, sobek=0)
    menu.add_command(label="Potong", perintah=lambda: widget_entri.event_generate("<<Potong>>"))
    menu.add_command(label="Salin", perintah=lambda: widget_entri.event_generate("<<Salin>>"))
    menu.add_command(label="Tempel", perintah=lambda: widget_entri.event_generate("<<Tempel>>"))
    menu.tambahkan_pemisah()
    menu.add_command(label="Pilih Semua", perintah=lambda: entry_widget.select_range(0, 'end'))

    def _show_menu(acara):
        menu.tk_popup(akar_peristiwa.x, akar_peristiwa.y)
    widget_entri.bind("<Tombol-3>", _tampilkan_menu)

    # Bind Ctrl+A untuk select all di Entry
    widget_entri.bind("<Control-a>", lambda e: widget_entri.pilih_rentang(0, 'akhir'))
    widget_entri.bind("<Control-c>", lambda e: widget_entri.event_generate("<<Salin>>"))
    widget_entri.bind("<Kontrol-v>", lambda e: widget_entri.event_generate("<<Tempel>>"))


Kita akan membuat fitur menu konteks (potong/salin/tempel/hapus/pilih semua), agar di bidang teks Bahan-bahan, Cara memasak, dan Hasil modifikasi,agar ketika teks di blok atau dengan pilih semua, dan area entri disentuh lagi, maka teks dapat di salin dan tempel kan ke tempat lain.
Atau ketika teks di blok atau dengan select all, dan area entry disentuh lagi, maka teks dapat di cut, di copy paste kan, atau di delete.

Mksudnya adalah:

  • Untuk Entry maupun Text widget → saat pengguna blok teks (manual atau via Select All) → lalu klik kanan → harus bisa Cut, Copy, Paste, Delete dengan benar.
  • Deleteini belum ada di versi sebelumnya, jadi saya tambahkan.
  • Jika tidak ada teks yang diblok, menu Cut/Copy/Delete otomatis diaktifkan (biar tidak error).

✅ Perbaikan Menu Konteks Lengkap (Entri & Teks):

def add_entry_context_menu(entry_widget):
    menu = tk.Menu(entry_widget, tearoff=0)

    def do_cut():
        entry_widget.event_generate("<<Cut>>")

    def do_copy():
        entry_widget.event_generate("<<Copy>>")

    def do_paste():
        entry_widget.event_generate("<<Paste>>")

    def do_delete():
        try:
            sel_start = entry_widget.index("sel.first")
            sel_end = entry_widget.index("sel.last")
            entry_widget.delete(sel_start, sel_end)
        except tk.TclError:
            pass  # tidak ada selection

    def do_select_all():
        entry_widget.select_range(0, 'end')
        entry_widget.icursor('end')

    # buat menu
    menu.add_command(label="Cut", command=do_cut)
    menu.add_command(label="Copy", command=do_copy)
    menu.add_command(label="Paste", command=do_paste)
    menu.add_command(label="Delete", command=do_delete)
    menu.add_separator()
    menu.add_command(label="Select All", command=do_select_all)

    def _show_menu(event):
        # enable/disable sesuai kondisi
        try:
            entry_widget.selection_get()
            menu.entryconfig("Cut", state="normal")
            menu.entryconfig("Copy", state="normal")
            menu.entryconfig("Delete", state="normal")
        except tk.TclError:
            menu.entryconfig("Cut", state="disabled")
            menu.entryconfig("Copy", state="disabled")
            menu.entryconfig("Delete", state="disabled")

        menu.tk_popup(event.x_root, event.y_root)

    entry_widget.bind("<Button-3>", _show_menu)  # klik kanan
    entry_widget.bind("<Control-a>", lambda e: do_select_all())
    entry_widget.bind("<Control-A>", lambda e: do_select_all())


def add_text_context_menu(text_widget):
    menu = tk.Menu(text_widget, tearoff=0)

    def do_cut():
        text_widget.event_generate("<<Cut>>")

    def do_copy():
        text_widget.event_generate("<<Copy>>")

    def do_paste():
        text_widget.event_generate("<<Paste>>")

    def do_delete():
        try:
            sel_start = text_widget.index("sel.first")
            sel_end = text_widget.index("sel.last")
            text_widget.delete(sel_start, sel_end)
        except tk.TclError:
            pass  # tidak ada selection

    def do_select_all():
        text_widget.tag_add("sel", "1.0", "end-1c")
        text_widget.mark_set("insert", "end-1c")

    # buat menu
    menu.add_command(label="Cut", command=do_cut)
    menu.add_command(label="Copy", command=do_copy)
    menu.add_command(label="Paste", command=do_paste)
    menu.add_command(label="Delete", command=do_delete)
    menu.add_separator()
    menu.add_command(label="Select All", command=do_select_all)

    def _show_menu(event):
        # enable/disable sesuai kondisi
        try:
            text_widget.get("sel.first", "sel.last")
            menu.entryconfig("Cut", state="normal")
            menu.entryconfig("Copy", state="normal")
            menu.entryconfig("Delete", state="normal")
        except tk.TclError:
            menu.entryconfig("Cut", state="disabled")
            menu.entryconfig("Copy", state="disabled")
            menu.entryconfig("Delete", state="disabled")

        menu.tk_popup(event.x_root, event.y_root)

    text_widget.bind("<Button-3>", _show_menu)   # klik kanan
    text_widget.bind("<Control-a>", lambda e: do_select_all())
    text_widget.bind("<Control-A>", lambda e: do_select_all())

🔑 Perubahan Utama:

  • Potong / Salin / Hapus hanya aktif jika ada teks yang diblok.
  • Hapus benar-benar menghapus blok teks yang dipilih.
  • Pilih Semua → blok semua isi, jadi langsung bisa di‐ Cut/Copy/Delete.
  • Bisa dipakai di Entry(Nama Resep) dan di Text(Bahan, Cara, Hasil).

Kemudian, k teks sudah di blok, atau etika teks sudah di "select all", lalu seharusnya area entry harus bisa disentuh kembali dan blok tidak hilang sehingga kita bisa mengedit teks (copy/paste/cut/delete). Untuk itu  kita perlu  mencegat event klik kiri  (  <Button-1>) → lalu  jangan hapus pilihan.

Secara default, di Tkinter , kalau teks di Entry atau Text sudah di‐blok, lalu pengguna klik kiri lagi ke dalam widget → blok hilang (selection clear). Itu memang perilaku bawaan Tkinter.

📌 Kalau kamu ingin blok tetap ada ketika disentuh kembali, kita perlu mencegat event klik kiri ( <Button-1>) → lalu jangan hapus pilihan .


🔧 Solusi: Menahan Pilihan Saat Klik Kiri

Untuk Entry :

def keep_selection_entry(entry_widget):
    def prevent_deselect(event):
        # kalau ada blok teks → cegah klik kiri menghapus selection
        if entry_widget.selection_present():
            return "break"  # hentikan event default
    entry_widget.bind("<Button-1>", prevent_deselect, add="+")

Untuk Text :

def keep_selection_text(text_widget):
    def prevent_deselect(event):
        try:
            text_widget.get("sel.first", "sel.last")  # kalau ada blok
            return "break"  # hentikan event default yang hapus selection
        except tk.TclError:
            pass
    text_widget.bind("<Button-1>", prevent_deselect, add="+")

🔄 Integrasi Dengan Konteks Menu

Setelah kamu membuat add_entry_context_menu(entry_widget)dan add_text_context_menu(text_widget), cukup hubungi juga:

add_entry_context_menu(nama_entry)
keep_selection_entry(nama_entry)

add_text_context_menu(bahan_text)
keep_selection_text(bahan_text)

add_text_context_menu(cara_text)
keep_selection_text(cara_text)

add_text_context_menu(hasil_text)
keep_selection_text(hasil_text)

✨ Hasil

  • Kalau teks diblok (manual atau via Select All ) → blok tetap ada meskipun pengguna klik lagi di dalam kolom.
  • Pengguna bisa langsung Potong, Salin, Hapus dari menu konteks tanpa kehilangan pilihan.
  • Pengeditan masih bisa jalan kalau pengguna mulai mengetik, blok otomatis diganti dengan teks baru.

Berikut ini contoh Menu konteks lengkap: Potong, Salin, Tempel, Hapus, Semua Pilih:

- Fix supaya blok teks (selection) tidak hilang kalau disentuh/klik lagi.

- Sudah diterapkan di semua field (Entri, Teks: Nama Resep, Bahan, Cara, Hasil).


# =============================
# Context Menu Helper
# =============================
def add_entry_context_menu(entry_widget):
    menu = tk.Menu(entry_widget, tearoff=0)
    menu.add_command(label="Cut", command=lambda: entry_widget.event_generate("<<Cut>>"))
    menu.add_command(label="Copy", command=lambda: entry_widget.event_generate("<<Copy>>"))
    menu.add_command(label="Paste", command=lambda: entry_widget.event_generate("<<Paste>>"))
    menu.add_command(label="Delete", command=lambda: entry_widget.delete("sel.first", "sel.last"))
    menu.add_separator()
    menu.add_command(label="Select All", command=lambda: entry_widget.select_range(0, 'end'))

    def _show_menu(event):
        menu.tk_popup(event.x_root, event.y_root)

    entry_widget.bind("<Button-3>", _show_menu)
    entry_widget.bind("<Control-a>", lambda e: entry_widget.select_range(0, 'end'))

    # cegah hilangnya blok saat klik lagi
    def prevent_deselect(event):
        if entry_widget.selection_present():
            return "break"
    entry_widget.bind("<Button-1>", prevent_deselect, add="+")

def add_text_context_menu(text_widget):
    menu = tk.Menu(text_widget, tearoff=0)
    menu.add_command(label="Cut", command=lambda: text_widget.event_generate("<<Cut>>"))
    menu.add_command(label="Copy", command=lambda: text_widget.event_generate("<<Copy>>"))
    menu.add_command(label="Paste", command=lambda: text_widget.event_generate("<<Paste>>"))
    menu.add_command(label="Delete", command=lambda: text_widget.delete("sel.first", "sel.last"))
    menu.add_separator()
    menu.add_command(label="Select All", command=lambda: text_widget.tag_add("sel", "1.0", "end"))

    def _show_menu(event):
        menu.tk_popup(event.x_root, event.y_root)

    text_widget.bind("<Button-3>", _show_menu)
    text_widget.bind("<Control-a>", lambda e: text_widget.tag_add("sel", "1.0", "end"))

    # cegah hilangnya blok saat klik lagi
    def prevent_deselect(event):
        try:
            text_widget.get("sel.first", "sel.last")
            return "break"
        except tk.TclError:
            pass
    text_widget.bind("<Button-1>", prevent_deselect, add="+")



⚡ Sekarang:

- Context menu sudah lengkap (Cut, Copy, Paste, Delete, Select All).

- Blok teks tidak hilang saat klik lagi.

- Tambahan tombol ⟳ Reset dan ↩ Kembali.

- Fitur inj bisa dikembangkan dengan ditambahkan shortcut Ctrl+C, Ctrl+X, Ctrl+V, Ctrl+Del, Ctrl+A agar lebih cepat.



Demikian, semoga bermanfaat.

Comments

Popular posts from this blog

SPLASH_SCREEN AWAL MEMULAI MEMBUAT APPLIKASI BERBASIS PYTHON