tkinter open file dialog

最簡易範例,不實用:

import tkinter as tk
from tkinter import filedialog

root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()
print(file_path)

實用的簡易範例:

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fd
from tkinter.messagebox import showinfo

# create the root window
root = tk.Tk()
root.title('Tkinter Open File Dialog')
root.resizable(False, False)
root.geometry('300x150')


def select_file():
    filetypes = (
        ('text files', '*.txt'),
        ('All files', '*.*')
    )

    filename = fd.askopenfilename(
        title='Open a file',
        initialdir='/',
        filetypes=filetypes)

    showinfo(
        title='Selected File',
        message=filename
    )


# open button
open_button = ttk.Button(
    root,
    text='Open a File',
    command=select_file
)

open_button.pack(expand=True)


# run the application
root.mainloop()

多選範例:

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fd
from tkinter.messagebox import showinfo

# create the root window
root = tk.Tk()
root.title('Tkinter File Dialog')
root.resizable(False, False)
root.geometry('300x150')


def select_files():
    filetypes = (
        ('text files', '*.txt'),
        ('All files', '*.*')
    )

    filenames = fd.askopenfilenames(
        title='Open files',
        initialdir='/',
        filetypes=filetypes)

    showinfo(
        title='Selected Files',
        message=filenames
    )


# open button
open_button = ttk.Button(
    root,
    text='Open Files',
    command=select_files
)

open_button.pack(expand=True)

root.mainloop()

the askopenfiles() function shows a file dialog and returns file objects of the selected files:

f = fd.askopenfiles()

Save File Dialog Box in Tkinter

在python 下的 tkinter 叫出 save as 對話框,取得要存檔的名稱。

from tkinter import *
from tkinter.filedialog import asksaveasfile

win= Tk()
win.geometry("750x250")
def save_file():
   f = asksaveasfile(initialfile = 'Untitled.txt',
defaultextension=".txt",filetypes=[("All Files","*.*"),("Text Documents","*.txt")])

btn= Button(win, text= "Save", command= lambda:save_file())
btn.pack(pady=10)

win.mainloop()

使用起來還滿簡單的。

範例2:

from tkinter import *
from tkinter import filedialog
#setting up parent window
root = Tk()

#function definition for opening file dialog
def savef():
    myFile = filedialog.asksaveasfile(mode='w', defaultextension='.txt')
    if myFile is None:
        return
    data = teditor.get(1.0,'end')
    myFile.write(data)
    myFile.close()

說明:asksaveasfile 會取得 file, asksaveasfilename 才是取得 string.

範例3:

from tkinter.filedialog import asksaveasfile
from tkinter import *

base = Tk()

def SaveFile():
   data = [('All tyes(*.*)', '*.*'),("csv file(*.csv)","*.csv")]
   file = asksaveasfilename(filetypes = data, defaultextension = data)
   # file will have file name provided by user.
   # Now we can use this file name to save file.
   with open(file,"w") as f:
      f.write(<data you want to save>)

save_btn = Button(base, text = 'Click to save file ', command = SaveFile)
save_btn.pack(side = TOP, pady = 20,padx = 50)

base.mainloop()

xcopy from wildcard directory fail

在 Windows 環境下的 xcopy 誤以為跟 linux 下的 cp 一樣好用, 結果並沒有, 只 copy 檔案時, 或某一個特定資料夾時是OK 的, 但遇到第一層就是 * 或 ? 的資料夾時,就會出意外了, 並不支援.

例如:

xcopy max* to D:\

如果目錄下有 max123.txt 與 maxABC folder, 則只會 copy max123.txt, wildcard match 不到 maxABC 的目錄.

解法有點複雜, 不是一般人能用第一印象記住的指令:

FOR /d %a in (.\max*) DO XCOPY "%a" D:\%a\ /s /y

上面是針對在 console 裡的語法, 寫進去 batch file 的話, 又不一樣! %a 要變成 %%a, 哎 microsoft 為什麼設計的這麼複雜, 應該是被古時候的架構綁住, 所以 % 已經被其他地方給使用, 又要讓舊的 script 可以正常執行, 又要讓新功能也可以執行, 變成現在這樣”怪怪的”.


最佳解答

Copying Files with Wildcards in the Path
https://stackoverflow.com/questions/8609028/copying-files-with-wildcards-in-the-path

Using XCOPY, it is no longer possible to use wildcarding in the file name for directories, and then just have a group of similarly named directories copied over to an archive or network structure.

(Hint: You want to do this all the time, if you are managing big wads of data. Eg. Video or movie files (where many files may be associated with the production. Or, price history data files, in various formats, or database files from various research projects, or experimental work.)

I am sure I used to be able to do this with older XCOPY, but with upgrades (for security), wildcards in the file name, do not work right (ie. you can build a directory structure, with the “/t” option, but you cannot get the damn files within the directories to migrate! Damned annoying.)

Well, we here at the Farm, always get to a solution. Even if it takes using a .44 magnum or a Styer 50. 🙂

So, heres what I came up with:

Assume you have a bunch of directories called “Fark_1, Fark_2, Fark-Special, Fark_in_the_Park, Fark99… ” and so on.

If all these “Fark…” directories exist on a C:\ disk, and you want to migrate a copy of this sub-structure to the archive disk on the network (say it is called: “h:”) you can just do the following:

Run a FOR DO from Windows (MS-DOS) command prompt:

 c:\>  XCOPY  fark*  h: /s /e /f /t

            The /s /e are for copying subdirs
            and empty directories, the /f says to
            show the files, and the /t says to only
            copy the disk directories.  No XCOPY
            option seems to exist anymore to copy the
            disk structure AND the files in the
            directories.

To get the files to copy over, you then do the following, also from Windows command prompt:

 FOR /d %a in (fark*) DO XCOPY "%a" h:%a\ /s /e /f /h /k /y

            The /d says we are using directory names.
            The %a is the variable name, substituted
            into the command script for XCOPY. Make
            sure to use whatever logical name or drive
            letter you have for your archive structure.
            (ie. don't use h: in the above command,
            unless you have mapped h: to the place on
            the network where you want to copy your
            files to.)  And note, the /y option means
            overwrite any files already there.

Yes, this is old-school unix/dos stuff, but it works, and you can put it into a batch job. And you can avoid being the mouseboy to get the work done. (Point, click, grunt, curse. Repeat. The canonical process for using a GUI, right?) Now, there are lots of fancy ways to do this. And various utils like Robocopy, XXcopy and so on. But this gets it done, and if you have any copy of Windows, you probably already have the XCOPY and FOR-DO thingys. All the best. Oh, if you slot the two commands into a batch job, then you have to adjust the syntax for two levels of command substitution (I think is the correct term).

batch example:

 REM --- copy the "fark*" files to the archive disk h:
 @echo off
 REM --- create/update the structure 
 xcopy fark* h: /s /e /f /t
 REM --- copy over the fark* dirs, and all associated subdirs
 for /d %%a in (fark*) do xcopy "%%a" "h:%%a\" /s /e /f /h /k /y

資料來源

xcopy from wildcard directory
https://stackoverflow.com/questions/23209474/xcopy-wildcard-source-folder-name-to-destination

Using Wildcards for Directories in Window’s copy Command
https://stackoverflow.com/questions/43243096/using-wildcards-for-directories-in-windows-copy-command

Java Long to String

出乎我的意料之外, 原來 long 轉 string 用 + 符號就可以了, 呵呵.

Using + operator

long var = 123L;
String str = var+""; // str is '123'

Long.toString()

long var = 12345L;
String str = Long.toString(var);
System.out.println(str); //prints '12345'

這個偶爾會用錯, 變成 Long(var).toString();

Long constructor with long argument has been deprecated in Java 9, but you should know it.

原來古時候, 是可以的.

String.valueOf()

long l = 12345L;
String str = String.valueOf(l); // str is '12345'

String.format()

long l = 369L;
String s = String.format(“%d”, l);

這些都沒有加號好用和直覺呀, 呵呵.

Java insert Mysql中文字亂碼問題

這個連線時,要設定utf8編碼格式有關。

     Connection mConn

     try {
         Class.forName("com.mysql.jdbc.Driver").newInstance();
         mConn = DriverManager.getConnection("jdbc:mysql://localhost:3306/database_name?useUnicode=true&characterEncoding=utf-8", "root", "password");
     } catch (Exception e) {
        e.printStackTrace();
     }

環境是:

  • Rocky linux 8.6
  • mysql Ver 8.0.30 for Linux on x86_64
  • java openjdk 19.0.2 2023-01-17

在 create table 時有指定:

 ENGINE=InnoDB DEFAULT CHARSET=utf

結果還是亂碼.

如果修改 Connector/J 的 url 加入 &characterEncoding=utf8mb4 會掛掉, 無法連線.

Connector/J did not support utf8mb4 for servers 5.5.2 and newer.
Connector/J now auto-detects servers configured with character_set_server=utf8mb4 or treats the Java encoding utf-8 passed using characterEncoding=… as utf8mb4 in the SET NAMES= calls it makes when establishing the connection. (Bug #54175)

透過 sql command 檢查編碼:

show variables like 'character%';

-- SHOW VARIABLES LIKE 'collation_%';

結果, 在 mysql 裡顯示是正確的… , 原來是輸出時 browser 的問題, browser 不知道內容的編碼, 所以沒猜到是中文. 在 servlet 裡指定輸出為 utf-8 後, 就一切正常了.

response.setCharacterEncoding("UTF-8");

附註: 這個 response.setCharacterEncoding(“UTF-8”); 要在一進 protected void doPost() or doGet() 就呼叫, 如果是在最後才設定, 在設定之前輸出的, 也會是亂碼! 真是太神奇了.

java Convert from ResultSet to JSON Object

使用起來滿簡單的, 把 ResultSet 換成 CachedRowSet, 程式也可以順利執行, 這樣就可以輕鬆把資料庫裡的內容倒出來了.

    public static JSONArray convertToJSONArray(ResultSet resultSet)
            throws Exception {
        JSONArray jsonArray = new JSONArray();
        while (resultSet.next()) {
            JSONObject obj = new JSONObject();
            int total_rows = resultSet.getMetaData().getColumnCount();
            for (int i = 0; i < total_rows; i++) {
                obj.put(resultSet.getMetaData().getColumnLabel(i + 1)
                        .toLowerCase(), resultSet.getObject(i + 1));

            }
            jsonArray.put(obj);
        }
        return jsonArray;
    }

資料來源:
https://stackoverflow.com/questions/58585734/convert-from-resultset-to-json-object

Get cookie value in jsp

read value

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%
String cookieName = "username";
Cookie[] cookies = request.getCookies();

if (cookies != null) {
 for (Cookie cookie : cookies) {
   if (cookie.getName().equals(cookieName)) {
     //do something
     //value can be retrieved using #cookie.getValue()
    }
  }
}
%>

set value

Cookie cookie = new Cookie ("username",username);
response.addCookie(cookie);

delete value

Cookie cookie = new Cookie ("username",username);
cookie.setMaxAge(0);
response.addCookie(cookie);

SendRedirect in java servlet

結論: 直接用 sendRedirect 就好了.

Redirect

protected void doGet(HttpServletRequest req, HttpServletResponse resp){
    resp.sendRedirect("http://www.google.com");  
}

Forward

protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
    RequestDispatcher dispatcher = getServletContext()
      .getRequestDispatcher("/servlet2");
    dispatcher.forward(req, resp);
}

forward() methodsendRedirect() method
The forward() method works at server side.The sendRedirect() method works at client side.
It sends the same request and response objects to another servlet.It always sends a new request.

Cookies in java Servlet

create Cookie

Cookie ck=new Cookie("user","usermax");
response.addCookie(ck);

delete Cookie

Cookie ck=new Cookie("user","");
ck.setMaxAge(0);
response.addCookie(ck);

設置將最長生存期:可以使用setMaxAge方法到指定多長時間(以秒為單位),在這時間內cookie應該是有效的。以下將成立一個24小時的cookie。

cookie.setMaxAge(60*60*24);

get Cookies

Cookie[] cookies = request.getCookies();

if (cookies != null) {
 for (Cookie cookie : cookies) {
   if (cookie.getName().equals("cookieName")) {
     //do something
     //value can be retrieved using #cookie.getValue()
    }
  }
}

or

for (Cookie cookie : cookies) {
    if (cookie.getName().equals("<NAME>")) {
        // do something here
    } 
    if (cookie.getName().equals("<ANOTHER_NAME>")) {
        // do something here
    } 
    // and so on...
}

or

// ...
Map<String, Cookie> cookieMap = new HashMap<>();
for (Cookie cookie : cookies) {
    cookieMap.put(cookie.getName(), cookie);
}

Cookie firstRequiredCookie = cookieMap.get("<NAME>");
// do something with firstRequiredCookie 
Cookie nextRequiredCookie = cookieMap.get("<ANOTHER_NAME>");
// do something with nextRequiredCookie 
// ...

Servlet Cookies 方法:
以下是有用的方法列表時,可以使用servlet操縱cookies。

S.N. Method & Description
1 public void setDomain(String pattern)
這個方法設置域的cookie適用,例如www.max-everyday.com
2 public String getDomain()
此方法獲取域的cookie應用,例如max-everyday.com
3 public void setMaxAge(int expiry)
此方法設置cookie過期之前多少時間(以秒為單位)間隔。
4 public int getMaxAge()
此方法返回的最大年齡(周期)cookie,以秒為單位指定,預設是-1表示cookie將永不刪。
5 public String getName()
此方法返回的cookie的名稱。創建後的名稱不能改變。
6 public void setValue(String newValue)
此方法設置的cookie值。
7 public String getValue()
這種方法得到的cookie關聯的值。
8 public void setPath(String uri)
這個方法設定這個cookie的路徑。
9 public String getPath()
這種方法得到這個cookie的路徑。
10 public void setSecure(boolean flag)
加密(如SSL)連接。
11 public void setComment(String purpose)
本方法規定了注釋,說明一個cookie的目的。
12 public String getComment()
此方法返回的注釋。

Generate a UUID in Java

example:

import java.util.UUID;

class MyUuidApp {
    public static void main(String[] args) {
        UUID uuid = UUID.randomUUID();
        String uuidAsString = uuid.toString();
        System.out.println("Your UUID is: " + uuidAsString);
    }
}

輸出的長像:

f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454

如果不要 -, 可以這樣寫:

final String uuid = UUID.randomUUID().toString().replace("-", "");
System.out.println("uuid = " + uuid);

Java UUID Class Methods

MethodsDescription
clockSequence()It returns the clock sequence value associated with this specified UUID.
compareTo()The method compares the UUID with the specific UUID.
equals()The method compares this object to the specified object.
fromString()It generates a UUID from the String representation.
getLeastSignificantBits()It returns the least significant 64 bits of this UUID’s 128-bit value.
getMostSignificantBits()It returns the most significant 64 bits of this UUID’s 128-bit value.
hashCode()It returns a hash code for this UUID.
nameUUIDFromBytes()It retrieves a version-3 (name-based) UUID based on the specified byte array.
node()It returns a node value that is associated with the specified UUID.
randomUUID()It returns a randomly generated UUID.
timestamp()It returns a timestamp value associated with this specified UUID.
toString()It returns a String object representing this UUID.
variant()It is used to get the variant associated with the specified UUID.
version()We can get the version number associated with the specified UUID.

因為 UUID 是要在DB裡識別用的,也可以透過Database 裡的UUID 相關函數來取得。