Android Native – RecyclerView swipe-to-remove and drag-to-reorder

Introduction

In this tutorial, we will learn how to add swipe-to-remove and drag-to-reorder functionalities into RecyclerView.

Goals

At the end of the tutorial, you would have learned:

  1. How to add swipe-to-remove and drag-to-reorder functionality to a RecyclerView.
Tools Required
  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 1.
Prerequisite Knowledge
  1. Basic Android.
  2. Basic RecyclerView.
Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Replace the content of activity_main.xml with the code below.

     <?xml version="1.0" encoding="utf-8"?>
     <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_constraintTop_toTopOf="parent" />
     </androidx.constraintlayout.widget.ConstraintLayout>
  3. Create a new layout resource called item_view.xml. This is the ViewHolders layout. Copy and paste the code below.

     <?xml version="1.0" encoding="utf-8"?>
     <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/itemView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:gravity="center"
        android:textSize="32sp"
        tools:text="TextView" />
  4. Create a new Kotlin file called Item.kt. Copy and paste the code below.

     data class Item(
        val content: String
     )
  5. Create a new Kotlin file called ItemAdapter.kt. Copy and paste the code below.

     import android.view.LayoutInflater
     import android.view.View
     import android.view.ViewGroup
     import android.widget.TextView
     import androidx.recyclerview.widget.RecyclerView
    
     class ItemAdapter(private val dataset: List<Item>): RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {
    
        class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
            val textView: TextView = itemView.findViewById(R.id.itemView)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.item_view, parent, false)
    
            return ItemViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
            holder.textView.text = dataset[position].content
        }
    
        override fun getItemCount() = dataset.size
     }
  6. In MainActivity#onCreate(), append the code below.

     //Creates a list of 10 elements
     val dataset = MutableList(10){
        Item("I am Item #$it")
     }
    
     //finds the RecyclerView
     val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
    
     //Assigns an adapter to RecyclerView
     recyclerView.adapter = ItemAdapter(dataset)
Project Overview

Currently, our project will compile and run just fine. All we have at the moment is a vanilla recycler with 10 items. As you can see from the Gif below with the pointer tracking option on, our RecyclerView currently does not register swiping and hold-to-drag actions.

VanillaRecycler.gif

At the end of the tutorial, we should be able to swipe to remove and hold-to-drag-to-reorder the RecyclerView.

ItemTouchHelper

ItemTouchHelper is a class provided by Android specifically to implement the functionalities that we want. Its most basic properties are:

  1. ItemTouchHelper.Callback: this is an abstract inner class. It is meant to be used only if you require fine control over the swipe and drop behaviors.
  2. ItemTouchHelper.SimpleCallback: this is also an abstract inner class. It already implements ItemTouchHelper.Callback, so we only have to implement two simple methods, onMove() and onSwipe(), to decide what to do when swiping and dragging actions are recorded.
  3. UP, DOWN, LEFT, RIGHT, START, END constants: These constants represent the actions that the user took, which the ItemTouchHelper.Callback class can use.

The picture below provides a rough picture of the relationship among the classes. LEFT and RIGHT are not listed because START and END can perform the same thing for both LTR and RTL layouts.

ItemTouchHelper.jpg

Implementing ItemTouchHelper.SimpleCallback

There are about 3 things that we need to perform to implement swipe-to-remove and drag-to-move.

  1. Create an ItemTouchHelper.SimpleCallback.
  2. Use that callback to create an ItemTouchHelper.
  3. Attaches the ItemTouchHelper to the RecyclerView.

Follow the steps below to implement ItemTouchHelper.SimpleCallback.

  1. Append the code below into MainActivity#onCreate(). Notice that we had to provide the bitwise or result of the direction constants.

     //Implementing the callback.
     val callback = object : ItemTouchHelper.SimpleCallback(
        ItemTouchHelper.UP or ItemTouchHelper.DOWN, //bitwise OR
        ItemTouchHelper.START or ItemTouchHelper.END //bitwise OR
     ) {
    
     }
  2. We have not implemented the required members yet, so let us start with the first required, onMove(). This function determines what to do during drag-and-drop behaviors. All we had to do was to get the adapter positions of the ViewHolders and then call the notifyItemMoved() publisher. When the RecyclerView receives that signal, it will automatically reorder the items for us. It is optional to update the original dataset for our tutorial App.

                override fun onMove(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder
                ): Boolean {
                    val fromPosition = viewHolder.adapterPosition
                    val toPosition = target.adapterPosition
    
                    //modifying the dataset as well is optional for this tutorial
     //                val movedItem = dataset.removeAt(fromPosition)
     //                dataset.add(toPosition, movedItem)
    
                    //push specific event
                    recyclerView.adapter?.notifyItemMoved(fromPosition, toPosition)
    
                    return true
                }
  3. Next, we will need to implement the other required member, onSwiped(). All we had to do was to remove the item at the requested position, and then call the notifyItemRemoved() publisher.

     override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        val position = viewHolder.adapterPosition
    
        //Actually removes the item from the dataset
        dataset.removeAt(position)
    
        //push specific event
        recyclerView.adapter?.notifyItemRemoved(position)
     }
Run the App

We only need two more lines of code before we can run the app.

  1. Create the ItemTouchHelper object.

     //Creates touch helper with callback
     val touchHelper = ItemTouchHelper(callback)
  2. Attach it to the RecyclerView.

     //attaches the helper to the recyclerView
     touchHelper.attachToRecyclerView(recyclerView)

Now, run the app. The swiping and dragging functions should work similarly to the Gif below.

SwipeDragRecycler.gif

Solution Code

MainActivity.kt

package com.codelab.daniwebrecyclerviewswipetoremove

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       //Creates a list of 10 elements
       val dataset = MutableList(10){
           Item("I am Item #$it")
       }

       //finds the RecyclerView
       val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)

       //Assigns an adapter to RecyclerView
       recyclerView.adapter = ItemAdapter(dataset)

       //Implementing the callback.
       val callback = object : ItemTouchHelper.SimpleCallback(
           ItemTouchHelper.UP or ItemTouchHelper.DOWN, //bitwise OR
           ItemTouchHelper.START or ItemTouchHelper.END //bitwise OR
       ) {
           override fun onMove(
               recyclerView: RecyclerView,
               viewHolder: RecyclerView.ViewHolder,
               target: RecyclerView.ViewHolder
           ): Boolean {
               val fromPosition = viewHolder.adapterPosition
               val toPosition = target.adapterPosition

               //modifying the dataset as well is optional for this tutorial
//                val movedItem = dataset.removeAt(fromPosition)
//                dataset.add(toPosition, movedItem)

               //push specific event
               recyclerView.adapter?.notifyItemMoved(fromPosition, toPosition)

               return true
           }

           override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
               val position = viewHolder.adapterPosition

               //Actually removes the item from the dataset
               dataset.removeAt(position)

               //push specific event
               recyclerView.adapter?.notifyItemRemoved(position)
           }

       }

       //Creates touch helper with callback
       val touchHelper = ItemTouchHelper(callback)

       //attaches the helper to the recyclerView
       touchHelper.attachToRecyclerView(recyclerView)
   }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/recycler_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
       app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

item_view.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/itemView"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_margin="16dp"
   android:gravity="center"
   android:textSize="32sp"
   tools:text="TextView" />

Item.kt

package com.codelab.daniwebrecyclerviewswipetoremove

data class Item(
   val content: String
)

ItemAdapter.kt

package com.codelab.daniwebrecyclerviewswipetoremove

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class ItemAdapter(private val dataset: List<Item>): RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {

   class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
       val textView: TextView = itemView.findViewById(R.id.itemView)
   }

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
       val view = LayoutInflater.from(parent.context)
           .inflate(R.layout.item_view, parent, false)

       return ItemViewHolder(view)
   }

   override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
       holder.textView.text = dataset[position].content
   }

   override fun getItemCount() = dataset.size
}
Summary

We have learned how to add swipe-to-remove and drag-to-reorder in this tutorial. The full project code can be found at https://github.com/dmitrilc/DaniwebRecyclerViewSwipeToRemove.

Create a fully redesigned tkinter window

After minimize the window disappear from taskbar and i can't set that again as it's looping...Is there any other way to minimize or fix that. watch inline code examples.

from tkinter import *
from PIL import Image, ImageTk
import time
from ctypes import windll

GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080

`Inline Code Example Here`
def set_appwindow(root):
    hwnd = windll.user32.GetParent(root.winfo_id())
    style = windll.user32.GetWindowLongA(hwnd, GWL_EXSTYLE)
    style = style & ~WS_EX_TOOLWINDOW
    style = style | WS_EX_APPWINDOW
    res = windll.user32.SetWindowLongA(hwnd, GWL_EXSTYLE, style)
    # re-assert the new window style
    root.wm_withdraw()
    root.after(10, lambda: root.wm_deiconify())
    print('done')

class modtk(Tk):
    toptitlebar = None
    maincanvaswindow = None
    resizeon = False
    dragging = False

    def updatetopbut(self, event):
        self.closebutton.config(fg=self.ttfg)
        self.fullscreenbutton.config(fg=self.ttfg)
        self.minimizebutton.config(fg=self.ttfg)

    def downdatetopbut(self, event):
        self.closebutton.config(fg=self.ttbg)
        self.fullscreenbutton.config(fg=self.ttbg)
        self.minimizebutton.config(fg=self.ttbg)

    def modify(self, title="tk", icon=None, titlebg="#ffffff", titlefg="#000000", bg="#EBEBEB"):
        self.tt = title
        self.ico = icon
        self.ttbg = titlebg
        self.ttfg = titlefg
        self.bgbg = bg
        self.title(title)
        self.width = 150
        self.height = 200
        self.maximized = False
        self.map = 0
        self.resizeside = None
        self.moveok = False
        self.minsizex = 0
        self.minsizey = 0
        self.resizeadjx = self.winfo_rootx()
        self.resizeadjy = self.winfo_rooty()
        self.update_idletasks()
        self.overrideredirect(True)
        self.config(bg=self.bgbg)
        if not icon == None:
            try:
                icon = Image.open(icon)
                icon = icon.resize((30, 30))
                icon = ImageTk.PhotoImage(icon)
            except Exception as e:
                print(e, "File should be a .png file.")
                icon = None
        __class__.toptitlebar = Frame(self, height=34, bd=0, highlightthickness=1, highlightbackground="#000000",
                                      bg=titlebg)
        __class__.toptitlebar.pack(side=TOP, fill=X)

        __class__.toptitlebar.bind("<ButtonPress-1>", self.SaveLastClickPos)
        __class__.toptitlebar.bind("<B1-Motion>", self.Dragging)
        __class__.toptitlebar.bind("<Double-Button-1>", self.maximize)
        __class__.toptitlebar.bind("<Enter>", self.updatetopbut)
        __class__.toptitlebar.bind("<Leave>", self.downdatetopbut)
        __class__.toptitlebar.bind("<ButtonRelease-1>", self.releaseresize)

        self.titlelabel = Label(__class__.toptitlebar, bg=titlebg, fg=titlefg, bd=0, padx=0, pady=0)
        self.titlelabel.image = icon
        self.titlelabel.config(image=icon, compound=LEFT, text="   %s" % title)
        self.titlelabel.place(x=0, y=0)

        self.titlelabel.bind("<ButtonPress-1>", self.SaveLastClickPos)
        self.titlelabel.bind("<B1-Motion>", self.Dragging)
        self.titlelabel.bind("<Double-Button-1>", self.maximize)
        self.closefunction = lambda event: self.destroy()

        self.closebutton = Label(__class__.toptitlebar, text="X",
                                 bd=0,
                                 bg=titlebg, fg=titlebg,
                                 padx=14, pady=7)
        self.closebutton.pack(side=RIGHT)

        self.closebutton.bind("<Enter>", lambda event: self.closebutton.config(bg="#FF0000"))
        self.closebutton.bind("<Leave>", lambda event: self.closebutton.config(bg=titlebg))
        self.closebutton.bind("<Button-1>", self.closefunction)

        self.fullscreenbutton = Label(__class__.toptitlebar, text="[]",
                                      bd=0,
                                      bg=titlebg, fg=titlebg,
                                      padx=14, pady=7)
        self.fullscreenbutton.pack(side=RIGHT)

        self.fullscreenbutton.bind("<Enter>", lambda event: self.fullscreenbutton.config(bg="#dddddd"))
        self.fullscreenbutton.bind("<Leave>", lambda event: self.fullscreenbutton.config(bg=titlebg))
        self.fullscreenbutton.bind("<Button-1>", self.maximize)

        self.minimizebutton = Label(__class__.toptitlebar, text="-",
                                    bd=0,
                                    bg=titlebg, fg=titlebg,
                                    padx=14, pady=7)
        self.minimizebutton.pack(side=RIGHT)

        self.minimizebutton.bind("<Enter>", lambda event: self.minimizebutton.config(bg="#dddddd"))
        self.minimizebutton.bind("<Leave>", lambda event: self.minimizebutton.config(bg=titlebg))
        self.minimizebutton.bind("<Button-1>", self.minimize)
        self.bind("<Map>", self.frame_mapped)

        __class__.maincanvaswindow = Canvas(self, width=self.width, height=self.height, bg=bg, highlightthickness=1,
                                            highlightbackground="#000000")
        __class__.maincanvaswindow.pack(side=TOP)

        self.bind("<Button-1>", self.startresize)
        self.bind("<B1-Motion>", self.resizewin)
        self.bind("<Motion>", self.isonresize)
        self.bind("<ButtonRelease-1>", self.releaseresize)

        self.lastClickX = 0
        self.lastClickY = 0
        self.map = 0
        self.maximized = False

    def modgeometry(self, w, h, x=None, y=None):
        w = int(w)
        h = int(h)
        self.width = w
        self.height = h
        if (__class__.maincanvaswindow.winfo_width() != w or __class__.maincanvaswindow.winfo_height() != h) and not __class__.resizeon and not __class__.dragging:
            __class__.maincanvaswindow.config(width=w, height=h)
        if not x == None or not y == None:
            x = int(x)
            y = int(y)
            self.geometry('%dx%d+%d+%d' % (w, (h + 33), x, y))
        else:
            self.geometry('%dx%d' % (w, (h + 33)))

    def SaveLastClickPos(self, event):
        if not self.maximized:
            if event.y > 5 and event.x > 5 and event.x < (self.winfo_width() - 5):
                self.lastClickX = event.x
                self.lastClickY = event.y
                self.moveok = True
            else:
                self.moveok = False

    def Dragging(self, event):
        if self.moveok:
            if not self.maximized:
                x = self.winfo_x() + (event.x - self.lastClickX)
                y = self.winfo_y() + (event.y - self.lastClickY)
                self.modgeometry(self.winfo_width(), (self.winfo_height() - 33), x, y)
`Inline Code Example Here`
    def frame_mapped(self, event=None):
        print(event)
        if self.map == 5:
            print("yes")
            self.update_idletasks()
            # self.deiconify()
            self.overrideredirect(True)
            self.map = 0
        else:
            self.map += 1
`Inline Code Example Here`
    def minimize(self, event=None):
        print("no")
        #self.map = 0
        self.update_idletasks()
        self.overrideredirect(False)
        self.iconify()

    def maximize(self, event=None):
        if not self.maximized:
            self.oldwidth = self.winfo_width()
            self.oldheight = self.winfo_height()
            self.oldx = self.winfo_x()
            self.oldy = self.winfo_y()
            self.maximized = True
            self.modgeometry(self.winfo_screenwidth(), (self.winfo_screenheight() - 33), 0, 0)
        else:
            self.maximized = False
            self.modgeometry(self.oldwidth, self.oldheight, self.oldx, self.oldy)

    def closefunc(self, func):
        self.closefunction = lambda event: func()
        self.closebutton.bind("<Button-1>", self.closefunction)

    def winfo_heightr(self):
        return (self.winfo_height() - 33)

    def startresize(self, event=None):
        print(event)
        if not self.maximized:
            #if event.widget == __class__.maincanvaswindow:
            if True:
                self.resizelastx = event.x
                self.resizelasty = event.y
                self.resizelastw = self.winfo_width()
                self.resizelasth = self.winfo_height() - 33
                self.resizeadjx = self.winfo_rootx()
                self.resizeadjy = self.winfo_rooty()
                __class__.resizeon = True
                print("canvas")
                nonety = True
##                if event.x < 5 and event.y < (self.winfo_height() - 33 -5):
##                    self.resizeside = "left"
##                    nonety = False
                if event.x > (self.winfo_width() - 5) and event.y < (self.winfo_height() - 33 -5):
                    self.resizeside = "right"
                    nonety = False
                if event.y >(self.winfo_height() -33 - 5) and event.x > 5 and event.x < (self.winfo_width() - 5):
                    self.resizeside = "down"
                    nonety = False
                if event.x > (self.winfo_width() - 5) and event.y > (self.winfo_height() - 33 - 5):
                    self.resizeside = "rightdown"
                    nonety = False
##                if event.x < 5 and event.y > (self.winfo_height() -33 - 5):
##                    self.resizeside = "leftdown"
##                    nonety = False
                if nonety:
                    self.resizeside = ""
                print(self.resizeside)
##            elif event.widget == __class__.toptitlebar:
##                self.resizelastx = event.x
##                self.resizelasty = event.y
##                self.resizelastw = self.winfo_width()
##                self.resizelasth = self.winfo_height() - 33
##                self.resizeadjx = self.winfo_rootx()
##                self.resizeadjy = self.winfo_rooty()
##                print("titlebar")
##                nonety = True
##                if event.x < 5 and event.y > 5:
##                    self.resizeside = "left"
##                    nonety = False
##                if event.y < 5 and event.x > 5 and event.x < (self.winfo_width() - 5):
##                    self.resizeside = "up"
##                    nonety = False
##                if event.x < 5 and event.y < 5:
##                    self.resizeside = "leftup"
##                    nonety = False
##                if event.x > (self.winfo_width() - 5) and event.y > 5:
##                    self.resizeside = "right"
##                    nonety = False
##                if event.x > (self.winfo_width() - 5) and event.y < 5:
##                    self.resizeside = "rightup"
##                    nonety = False
##                if nonety:
##                    self.resizeside = ""
##            else:
##                self.resizeside = ""

    def isonresize(self, event=None):
        print(event)
        if not self.maximized:
            #if event.widget == __class__.maincanvaswindow:
            if True:
                print("canvas")
                nonety = True
##                if event.x < 5:
##                    self.config(cursor="size_we")
##                    nonety = False
                if event.x > (self.winfo_width() - 5):
                    self.config(cursor="size_we")
                    nonety = False
                if event.y >(self.winfo_height() - 33 - 5):
                    self.config(cursor="size_ns")
                    nonety = False
                if event.x > (self.winfo_width() - 5) and event.y > (self.winfo_height() - 33 - 5):
                    self.config(cursor="size_nw_se")
                    nonety = False
##                if event.x < 5 and event.y > (self.winfo_height() - 33 - 5):
##                    self.config(cursor="size_ne_sw")
##                    nonety = False
                if nonety:
                    self.config(cursor="")
##            elif event.widget == __class__.toptitlebar:
##                print("titlebar")
##                nonety = True
##                if event.x < 5 and event.y > 5:
##                    self.config(cursor="size_we")
##                    nonety = False
##                if event.y < 5 and event.x > 5 and event.x < (self.winfo_width() - 5):
##                    self.config(cursor="size_ns")
##                    nonety = False
##                if event.x < 5 and event.y < 5:
##                    self.config(cursor="size_nw_se")
##                    nonety = False
##                if event.x > (self.winfo_width() - 5) and event.y > 5:
##                    self.config(cursor="size_we")
##                    nonety = False
##                if event.x > (self.winfo_width() - 5) and event.y < 5:
##                    self.config(cursor="size_ne_sw")
##                    nonety = False
##                if nonety:
##                    self.config(cursor="")
##            else:
##                self.config(cursor="")

    def resizewin(self, event=None):
        print(event)
        if not self.maximized:
            if self.resizeside == "right":
                neww = self.width + (event.x - self.resizelastx)
                print(self.resizeside)
                if neww < self.minsizex:
                    neww = self.minsizex
                self.resizelastx = neww
                self.modgeometry(neww, self.resizelasth)
            if self.resizeside == "rightdown":
                print(self.resizeside)
                neww = self.width + (event.x - self.resizelastx)
                newh = self.height + (event.y - self.resizelasty)
                if neww < self.minsizex:
                    neww = self.minsizex
                if newh < self.minsizey:
                    newh = self.minsizey
                self.resizelastx = neww
                self.resizelasty = newh
                self.modgeometry(neww, newh)
            if self.resizeside == "down":
                newh = self.height + (event.y - self.resizelasty)
                if newh < self.minsizey:
                    newh = self.minsizey
                self.resizelasty = newh
                self.modgeometry(self.resizelastw, newh)
            if self.resizeside == "rightup":
                print(self.resizeside)
                neww = self.width + (event.x - self.resizelastx)
                newh = self.resizelasth + (self.resizelasty - event.y)
                if neww < self.minsizex:
                    neww = self.minsizex
                if newh < self.minsizey:
                    newh = self.minsizey
                self.resizelastx = neww
                newy = self.resizeadjy - (self.resizelasty - event.y)
                newx = self.resizeadjx
                self.modgeometry(neww, newh, newx, newy)
            if self.resizeside == "up":
                print(self.resizeside)
                newh = self.resizelasth + (self.resizelasty - event.y)
                if newh < self.minsizey:
                    newh = self.minsizey
                newy = self.resizeadjy - (self.resizelasty - event.y)
                newx = self.resizeadjx
                self.modgeometry(self.resizelastw, newh, newx, newy)
            if self.resizeside == "leftup":
                print(self.resizeside)
                neww = self.resizelastw + (self.resizelastx - event.x)
                newh = self.resizelasth + (self.resizelasty - event.y)
                if neww < self.minsizex:
                    neww = self.minsizex
                if newh < self.minsizey:
                    newh = self.minsizey
                newy = self.resizeadjy - (self.resizelasty - event.y)
                newx = self.resizeadjx - (self.resizelastx - event.x)
                self.modgeometry(neww, newh, newx, newy)
            if self.resizeside == "left":
                print(self.resizeside)
                neww = self.resizelastw + (self.resizelastx - event.x)
                newx = self.resizeadjx - (self.resizelastx - event.x)
                if neww < self.minsizex:
                    neww = self.minsizex
                    newx = self.resizeadjx
                newy = self.resizeadjy
                self.modgeometry(neww, self.resizelasth, newx, newy)
            if self.resizeside == "leftdown":
                print(self.resizeside)
                neww = self.resizelastw + (self.resizelastx - event.x)
                newh = self.height + (event.y - self.resizelasty)
                if neww < self.minsizex:
                    neww = self.minsizex
                if newh < self.minsizey:
                    newh = self.minsizey
                self.resizelasty = newh
                newx = self.resizeadjx - (self.resizelastx - event.x)
                newy = self.resizeadjy
                self.modgeometry(neww, self.resizelasth, newx, newy)

    def releaseresize(self, event=None):
        if __class__.resizeon:
            __class__.resizeon = False
            self.width = self.winfo_width()
            self.height = self.winfo_heightr()
            __class__.maincanvaswindow.config(width=self.winfo_width(), height=self.winfo_heightr())
        if __class__.dragging:
            __class__.dragging = False

    def modminsize(self, event=None, w=None, h=None):
        if w != None:
            self.minsizex = w
        if h != None:
            self.minsizey = h

    def updatewin(self):
        self.update()

    def wmroot(self):
        return __class__.maincanvaswindow

    def retwm(self):
        return self

if __name__ == "__main__":
    root = modtk()
    root.modify(title="Modified window", titlebg="#000427", titlefg="#ffffff")
    root.modgeometry(600, 400)
    root.modminsize(600, 400)
    set_appwindow(root)

    label01 = Label(root.wmroot(), text="This works")

    def closewin():
        root.destroy()
        quit()

    root.closefunc(closewin)

    windowExit = False

    while not windowExit:

        label01.place(x=(root.winfo_width() // 2), y=(root.winfo_heightr() // 2))

        root.updatewin()

        time.sleep(0.05)

closewin()

How to move tkinter window | override-redirect flag

I have already created functions to handle that. But it's shacking when i try to move( After add more widgets). Can somebody help me with that.

from tkinter import *
from PIL import Image, ImageTk
import time

class modtk(Tk):
    toptitlebar = None
    maincanvaswindow = None
    resizeon = False
    dragging = False

    def updatetopbut(self, event):
        self.closebutton.config(fg=self.ttfg)
        self.fullscreenbutton.config(fg=self.ttfg)
        self.minimizebutton.config(fg=self.ttfg)

    def downdatetopbut(self, event):
        self.closebutton.config(fg=self.ttbg)
        self.fullscreenbutton.config(fg=self.ttbg)
        self.minimizebutton.config(fg=self.ttbg)

    def modify(self, title="tk", icon=None, titlebg="#ffffff", titlefg="#000000", bg="#EBEBEB"):
        self.tt = title
        self.ico = icon
        self.ttbg = titlebg
        self.ttfg = titlefg
        self.bgbg = bg
        self.title(title)
        self.width = 150
        self.height = 200
        self.maximized = False
        self.map = 0
        self.resizeside = None
        self.moveok = False
        self.minsizex = 0
        self.minsizey = 0
        self.resizeadjx = self.winfo_rootx()
        self.resizeadjy = self.winfo_rooty()
        self.update_idletasks()
        self.overrideredirect(True)
        self.config(bg=self.bgbg)
        if not icon == None:
            try:
                icon = Image.open(icon)
                icon = icon.resize((30, 30))
                icon = ImageTk.PhotoImage(icon)
            except Exception as e:
                print(e, "File should be a .png file.")
                icon = None
        __class__.toptitlebar = Frame(self, height=34, bd=0, highlightthickness=1, highlightbackground="#000000",
                                      bg=titlebg)
        __class__.toptitlebar.pack(side=TOP, fill=X)

        __class__.toptitlebar.bind("<ButtonPress-1>", self.SaveLastClickPos)
        __class__.toptitlebar.bind("<B1-Motion>", self.Dragging)
        __class__.toptitlebar.bind("<Double-Button-1>", self.maximize)
        __class__.toptitlebar.bind("<Enter>", self.updatetopbut)
        __class__.toptitlebar.bind("<Leave>", self.downdatetopbut)
        __class__.toptitlebar.bind("<ButtonRelease-1>", self.releaseresize)

        self.titlelabel = Label(__class__.toptitlebar, bg=titlebg, fg=titlefg, bd=0, padx=0, pady=0)
        self.titlelabel.image = icon
        self.titlelabel.config(image=icon, compound=LEFT, text="   %s" % title)
        self.titlelabel.place(x=0, y=0)

        self.titlelabel.bind("<ButtonPress-1>", self.SaveLastClickPos)
        self.titlelabel.bind("<B1-Motion>", self.Dragging)
        self.titlelabel.bind("<Double-Button-1>", self.maximize)
        self.closefunction = lambda event: self.destroy()

        self.closebutton = Label(__class__.toptitlebar, text="X",
                                 bd=0,
                                 bg=titlebg, fg=titlebg,
                                 padx=14, pady=7)
        self.closebutton.pack(side=RIGHT)

        self.closebutton.bind("<Enter>", lambda event: self.closebutton.config(bg="#FF0000"))
        self.closebutton.bind("<Leave>", lambda event: self.closebutton.config(bg=titlebg))
        self.closebutton.bind("<Button-1>", self.closefunction)

        self.fullscreenbutton = Label(__class__.toptitlebar, text="[]",
                                      bd=0,
                                      bg=titlebg, fg=titlebg,
                                      padx=14, pady=7)
        self.fullscreenbutton.pack(side=RIGHT)

        self.fullscreenbutton.bind("<Enter>", lambda event: self.fullscreenbutton.config(bg="#dddddd"))
        self.fullscreenbutton.bind("<Leave>", lambda event: self.fullscreenbutton.config(bg=titlebg))
        self.fullscreenbutton.bind("<Button-1>", self.maximize)

        self.minimizebutton = Label(__class__.toptitlebar, text="-",
                                    bd=0,
                                    bg=titlebg, fg=titlebg,
                                    padx=14, pady=7)
        self.minimizebutton.pack(side=RIGHT)

        self.minimizebutton.bind("<Enter>", lambda event: self.minimizebutton.config(bg="#dddddd"))
        self.minimizebutton.bind("<Leave>", lambda event: self.minimizebutton.config(bg=titlebg))
        self.minimizebutton.bind("<Button-1>", self.minimize)
        self.bind("<Map>", self.frame_mapped)

        __class__.maincanvaswindow = Canvas(self, width=self.width, height=self.height, bg=bg, highlightthickness=1,
                                            highlightbackground="#000000")
        __class__.maincanvaswindow.pack(side=TOP)

        self.bind("<Button-1>", self.startresize)
        self.bind("<B1-Motion>", self.resizewin)
        self.bind("<Motion>", self.isonresize)
        self.bind("<ButtonRelease-1>", self.releaseresize)

        self.lastClickX = 0
        self.lastClickY = 0
        self.map = 0
        self.maximized = False
`Inline Code Example Here`
    def modgeometry(self, w, h, x=None, y=None):
        w = int(w)
        h = int(h)
        self.width = w
        self.height = h
        if (__class__.maincanvaswindow.winfo_width() != w or __class__.maincanvaswindow.winfo_height() != h) and not __class__.resizeon and not __class__.dragging:
            __class__.maincanvaswindow.config(width=w, height=h)
        if not x == None or not y == None:
            x = int(x)
            y = int(y)
            self.geometry('%dx%d+%d+%d' % (w, (h + 33), x, y))
        else:
            self.geometry('%dx%d' % (w, (h + 33)))
`Inline Code Example Here`
    def SaveLastClickPos(self, event):
        if not self.maximized:
            if event.y > 5 and event.x > 5 and event.x < (self.winfo_width() - 5):
                self.lastClickX = event.x
                self.lastClickY = event.y
                self.moveok = True
            else:
                self.moveok = False
`Inline Code Example Here`
    def Dragging(self, event):
        if self.moveok:
            if not self.maximized:
                x = self.winfo_x() + (event.x - self.lastClickX)
                y = self.winfo_y() + (event.y - self.lastClickY)
                self.modgeometry(self.winfo_width(), (self.winfo_height() - 33), x, y)

    def frame_mapped(self, event=None):
        print(event)
        if self.map == 5:
            print("yes")
            self.update_idletasks()
            # self.deiconify()
            self.overrideredirect(True)
            self.map = 0
        else:
            self.map += 1

    def minimize(self, event=None):
        print("no")
        #self.map = 0
        self.update_idletasks()
        self.overrideredirect(False)
        self.iconify()

    def maximize(self, event=None):
        if not self.maximized:
            self.oldwidth = self.winfo_width()
            self.oldheight = self.winfo_height()
            self.oldx = self.winfo_x()
            self.oldy = self.winfo_y()
            self.maximized = True
            self.modgeometry(self.winfo_screenwidth(), (self.winfo_screenheight() - 33), 0, 0)
        else:
            self.maximized = False
            self.modgeometry(self.oldwidth, self.oldheight, self.oldx, self.oldy)

    def closefunc(self, func):
        self.closefunction = lambda event: func()
        self.closebutton.bind("<Button-1>", self.closefunction)

    def winfo_heightr(self):
        return (self.winfo_height() - 33)

    def startresize(self, event=None):
        print(event)
        if not self.maximized:
            #if event.widget == __class__.maincanvaswindow:
            if True:
                self.resizelastx = event.x
                self.resizelasty = event.y
                self.resizelastw = self.winfo_width()
                self.resizelasth = self.winfo_height() - 33
                self.resizeadjx = self.winfo_rootx()
                self.resizeadjy = self.winfo_rooty()
                __class__.resizeon = True
                print("canvas")
                nonety = True
##                if event.x < 5 and event.y < (self.winfo_height() - 33 -5):
##                    self.resizeside = "left"
##                    nonety = False
                if event.x > (self.winfo_width() - 5) and event.y < (self.winfo_height() - 33 -5):
                    self.resizeside = "right"
                    nonety = False
                if event.y >(self.winfo_height() -33 - 5) and event.x > 5 and event.x < (self.winfo_width() - 5):
                    self.resizeside = "down"
                    nonety = False
                if event.x > (self.winfo_width() - 5) and event.y > (self.winfo_height() - 33 - 5):
                    self.resizeside = "rightdown"
                    nonety = False
##                if event.x < 5 and event.y > (self.winfo_height() -33 - 5):
##                    self.resizeside = "leftdown"
##                    nonety = False
                if nonety:
                    self.resizeside = ""
                print(self.resizeside)
##            elif event.widget == __class__.toptitlebar:
##                self.resizelastx = event.x
##                self.resizelasty = event.y
##                self.resizelastw = self.winfo_width()
##                self.resizelasth = self.winfo_height() - 33
##                self.resizeadjx = self.winfo_rootx()
##                self.resizeadjy = self.winfo_rooty()
##                print("titlebar")
##                nonety = True
##                if event.x < 5 and event.y > 5:
##                    self.resizeside = "left"
##                    nonety = False
##                if event.y < 5 and event.x > 5 and event.x < (self.winfo_width() - 5):
##                    self.resizeside = "up"
##                    nonety = False
##                if event.x < 5 and event.y < 5:
##                    self.resizeside = "leftup"
##                    nonety = False
##                if event.x > (self.winfo_width() - 5) and event.y > 5:
##                    self.resizeside = "right"
##                    nonety = False
##                if event.x > (self.winfo_width() - 5) and event.y < 5:
##                    self.resizeside = "rightup"
##                    nonety = False
##                if nonety:
##                    self.resizeside = ""
##            else:
##                self.resizeside = ""

    def isonresize(self, event=None):
        print(event)
        if not self.maximized:
            #if event.widget == __class__.maincanvaswindow:
            if True:
                print("canvas")
                nonety = True
##                if event.x < 5:
##                    self.config(cursor="size_we")
##                    nonety = False
                if event.x > (self.winfo_width() - 5):
                    self.config(cursor="size_we")
                    nonety = False
                if event.y >(self.winfo_height() - 33 - 5):
                    self.config(cursor="size_ns")
                    nonety = False
                if event.x > (self.winfo_width() - 5) and event.y > (self.winfo_height() - 33 - 5):
                    self.config(cursor="size_nw_se")
                    nonety = False
##                if event.x < 5 and event.y > (self.winfo_height() - 33 - 5):
##                    self.config(cursor="size_ne_sw")
##                    nonety = False
                if nonety:
                    self.config(cursor="")
##            elif event.widget == __class__.toptitlebar:
##                print("titlebar")
##                nonety = True
##                if event.x < 5 and event.y > 5:
##                    self.config(cursor="size_we")
##                    nonety = False
##                if event.y < 5 and event.x > 5 and event.x < (self.winfo_width() - 5):
##                    self.config(cursor="size_ns")
##                    nonety = False
##                if event.x < 5 and event.y < 5:
##                    self.config(cursor="size_nw_se")
##                    nonety = False
##                if event.x > (self.winfo_width() - 5) and event.y > 5:
##                    self.config(cursor="size_we")
##                    nonety = False
##                if event.x > (self.winfo_width() - 5) and event.y < 5:
##                    self.config(cursor="size_ne_sw")
##                    nonety = False
##                if nonety:
##                    self.config(cursor="")
##            else:
##                self.config(cursor="")

    def resizewin(self, event=None):
        print(event)
        if not self.maximized:
            if self.resizeside == "right":
                neww = self.width + (event.x - self.resizelastx)
                print(self.resizeside)
                if neww < self.minsizex:
                    neww = self.minsizex
                self.resizelastx = neww
                self.modgeometry(neww, self.resizelasth)
            if self.resizeside == "rightdown":
                print(self.resizeside)
                neww = self.width + (event.x - self.resizelastx)
                newh = self.height + (event.y - self.resizelasty)
                if neww < self.minsizex:
                    neww = self.minsizex
                if newh < self.minsizey:
                    newh = self.minsizey
                self.resizelastx = neww
                self.resizelasty = newh
                self.modgeometry(neww, newh)
            if self.resizeside == "down":
                newh = self.height + (event.y - self.resizelasty)
                if newh < self.minsizey:
                    newh = self.minsizey
                self.resizelasty = newh
                self.modgeometry(self.resizelastw, newh)
            if self.resizeside == "rightup":
                print(self.resizeside)
                neww = self.width + (event.x - self.resizelastx)
                newh = self.resizelasth + (self.resizelasty - event.y)
                if neww < self.minsizex:
                    neww = self.minsizex
                if newh < self.minsizey:
                    newh = self.minsizey
                self.resizelastx = neww
                newy = self.resizeadjy - (self.resizelasty - event.y)
                newx = self.resizeadjx
                self.modgeometry(neww, newh, newx, newy)
            if self.resizeside == "up":
                print(self.resizeside)
                newh = self.resizelasth + (self.resizelasty - event.y)
                if newh < self.minsizey:
                    newh = self.minsizey
                newy = self.resizeadjy - (self.resizelasty - event.y)
                newx = self.resizeadjx
                self.modgeometry(self.resizelastw, newh, newx, newy)
            if self.resizeside == "leftup":
                print(self.resizeside)
                neww = self.resizelastw + (self.resizelastx - event.x)
                newh = self.resizelasth + (self.resizelasty - event.y)
                if neww < self.minsizex:
                    neww = self.minsizex
                if newh < self.minsizey:
                    newh = self.minsizey
                newy = self.resizeadjy - (self.resizelasty - event.y)
                newx = self.resizeadjx - (self.resizelastx - event.x)
                self.modgeometry(neww, newh, newx, newy)
            if self.resizeside == "left":
                print(self.resizeside)
                neww = self.resizelastw + (self.resizelastx - event.x)
                newx = self.resizeadjx - (self.resizelastx - event.x)
                if neww < self.minsizex:
                    neww = self.minsizex
                    newx = self.resizeadjx
                newy = self.resizeadjy
                self.modgeometry(neww, self.resizelasth, newx, newy)
            if self.resizeside == "leftdown":
                print(self.resizeside)
                neww = self.resizelastw + (self.resizelastx - event.x)
                newh = self.height + (event.y - self.resizelasty)
                if neww < self.minsizex:
                    neww = self.minsizex
                if newh < self.minsizey:
                    newh = self.minsizey
                self.resizelasty = newh
                newx = self.resizeadjx - (self.resizelastx - event.x)
                newy = self.resizeadjy
                self.modgeometry(neww, self.resizelasth, newx, newy)

    def releaseresize(self, event=None):
        if __class__.resizeon:
            __class__.resizeon = False
            self.width = self.winfo_width()
            self.height = self.winfo_heightr()
            __class__.maincanvaswindow.config(width=self.winfo_width(), height=self.winfo_heightr())
        if __class__.dragging:
            __class__.dragging = False

    def modminsize(self, event=None, w=None, h=None):
        if w != None:
            self.minsizex = w
        if h != None:
            self.minsizey = h

    def updatewin(self):
        self.update()

    def wmroot(self):
        return __class__.maincanvaswindow

    def retwm(self):
        return self

if __name__ == "__main__":
    root = modtk()
    root.modify(title="Modified window", titlebg="#000427", titlefg="#ffffff")
    root.modgeometry(600, 400)
    root.modminsize(600, 400)

    label01 = Label(root.wmroot(), text="This works")

    def closewin():
        root.destroy()
        quit()

    root.closefunc(closewin)

    windowExit = False

    while not windowExit:

        label01.place(x=(root.winfo_width() // 2), y=(root.winfo_heightr() // 2))

        root.updatewin()

        time.sleep(0.05)

closewin()

Database Fundamentals #21: Using the JOIN Operator, OUTER JOIN

The OUTER JOIN returns one complete set of data and then the matching values from the other set. The syntax is basically the same as INNER JOIN but you have to include whether or not you're dealing with a RIGHT or a LEFT JOIN. The OUTER word, just like the INNER key word, is not required.

OUTER JOIN

Imagine a situation where you have a list of people. Some of those people have financial transactions, but some do not. If you want a query that lists all people in the system, including those with financial transactions, the query might look like this: