添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
class BWidget(QWidget): def __init__(self, parent=None): super(BWidget, self).__init__(parent) self.setAcceptDrops(True)

需要注意的是,上述的這些 QEvent 函式 都是私有的 ,意即無法(Python 其實可以強制設定,但是很難看)透過外部撰寫。

上面這句話的意思是, 不能用 QtDesigner 來佈置這些 Widget! 請按部就班繼承改寫新 Class,用指令插入 Layout 裡。

這邊就不再介紹如何手動放這些 Widget 了,較不瞭解的初學者可以用 QtDesigner 拉出 QLayout 後用 insertWidget 指令達成。

抓起?(AWidget:: mousePressEvent / mouseReleaseEvent

這個函式專門處理 AWidget 中任何按下滑鼠的動作,因此如果你的 AWidget 並非原始無瑕的 QWidget,那 Qt 早已設定過一些互動函式了。

在 Python 中,子 Class 的新 method 若和任何父項 method 撞名,會造成覆蓋。為了不讓 Qt 的設定功能失效,請使用 super 函式讓 Qt 先做完應有的工作。

class AWidget(QWidget): def mousePressEvent(self, event): super(AWidget, self).mousePressEvent(event) def mouseReleaseEvent(self, event): super(AWidget, self).mouseReleaseEvent(event)

接下來的 QEvent 函式都是一樣,如果不小心蓋掉發生怪怪的狀況,例如 QTableWidget 無法再選起儲存格,那就加上 super 補上被忽略的工作。

言歸正傳,由於「按下滑鼠」這個事件不一定是想抓東西來拖移,因此不能就這樣亂槍打鳥,要先偷偷紀錄其的行為。在這裡設定一個名稱「draged」紀錄滑鼠的動作,決定是否為滑鼠左鍵抓住的動作。

放掉滑鼠的話就把這個值取消,表示使用者並沒有做出拖動的行為。

class AWidget(QWidget): def __init__(self, parent=None): super(AWidget, self).__init__(parent) self.draged = False def mousePressEvent(self, event): super(AWidget, self).mousePressEvent(event) if event.button()==Qt.LeftButton: self.draged = True def mouseReleaseEvent(self, event): super(AWidget, self).mouseReleaseEvent(event) self.draged = False

移出(AWidget:: mouseMoveEvent

這個函式專門處理 AWidget 中滑鼠移動的動作,也包括憑空移動,因此剛才設定的「draged」可以告訴我們,使用者正想要「拿著東西移動」。

既然想拿東西,就必須把東西打包交給使用者,一般普遍為文字資訊,包裝後在 BWidget 解開,亦可以攜帶圖片、網址、HTML、顏色資訊。

這裡介紹兩個新的 Qt Class:

QMimeData:行李箱,將一堆雜亂的行李分裝後包起來。

QDrag:送貨員,可以附上一張圖片以辨識該行李的內容,另外送貨員知道寄件者地址(AWidget)、滑鼠停駐點等資訊,也可以幫旅行中的滑鼠換外觀。

接下來就來裝箱吧,根據 Pyslvs 的影片,我們可以推測出攜帶的資訊為 AWidget 的「被選擇行號」,因此透過一段小迴圈篩選出所有被選擇的行號。

用 Python 的 set 集合型態可以將重複選取的內容排除掉,接著重新排序轉回 list 類型儲存。

而 QTableWidget 的 mouseMoveEvent 其實有「按住連續選取」的功能,但是這樣會干擾我們拖移的行為,因此這邊不使用 super。

class AWidget(QTableWidget): def selectedRows(self): a = list() for r in self.selectedRanges(): a += [i for i in range(r.topRow(), r.bottomRow()+1)] return sorted(set(a)) def mouseMoveEvent(self, event): if self.draged: selectedRows = self.selectedRows() selectedRowCount = len(selectedRows) if selectedRowCount==2 or selectedRowCount==3: drag = QDrag(self) mimeData = QMimeData() mimeData.setText(';'.join([str(e) for e in selectedRows])) drag.setMimeData(mimeData) drag.setPixmap(QPixmap(":/icons/tooltips/need{}bearings.png".format(selectedRowCount)).scaledToWidth(50)) drag.exec_()

由於下方的表格需要 2 或 3 個選取的行號,因此以外的選項我們一律不接受。

行號資訊型態是 int,這邊轉換成字串後用分號 ; 包裝起來。而這裡還有設定 need{}bearings.png 的圖片,表示拿了 2 個或 3 個「軸承」。

最後下的 drag.exec_() 方法為延遲函式,表示送貨員已出發,它會一直等到貨物到達或被丟棄時才會回傳,因此要注意不要讓執行序被阻塞。

移入(BWidget:: dragEnterEvent

這個函式專門處理 BWidget 中滑鼠 帶著資訊進入 的動作。這裡的 event 物件已經被 Qt 轉換成我們的送貨員,與剛才的 QDrag 物件有一定程度的相仿。要使用審核機制來判別這個送貨員是不是我們要接受的對象。

要如何審核呢?有兩種結果:

使用 acceptProposedAction 方法接受這名送貨員進入 BWidget 的領域。

使用一般的 ignore 方法無視這名送貨員,或是乾脆不理他,會造成滑鼠游標出現類似禁止的符號,依你的桌面系統而定。

被允許的送貨員能在進入的時候,依你的桌面系統出現類似抓著資訊的樣式,這時可以選擇是否放開以丟下資訊,或是將資訊帶走(我只是路過),不一定會送達 BWidget。

若是送貨員在此處被禁止投遞,卻仍然放開滑鼠,那他手中的資訊就會遺失,而且會無法重新取得(除非回到 AWidget 再打包)。

class BWidget(QTableWidget): def dragEnterEvent(self, event): mimeData = event.mimeData() if mimeData.hasText(): if len(mimeData.text().split(';'))==self.bearings: event.acceptProposedAction()

這邊的程式中, self.bearings 為允許的行號數,例如連桿表格為 2 個,當拆解用 ; 分號封裝的字串時,數量為 2 個,那此貨物就允許進入。

橫越(BWidget:: dragMoveEvent

某些列表式的 QWidget,例如 QTableView、QListView、QTreeView,其實會內建清單拖移功能,讓使用者可以直接拖動儲存格,插到自己或是其他相同類型的 QWidget 中,而且不用自己寫 QEvent 函式,只是這些功能預設是關閉的。

由於上述原因,我們得將自訂送貨員打扮成上面較特殊 QWidget 的送貨員,否則這些類型會不允許他送進資訊,即使在上一小節中已經允許。

class BWidget(QTableWidget): def dragMoveEvent(self, event): event.setDropAction(Qt.MoveAction) event.accept()

這些特殊 QWidget 的送貨員會攜帶「放置行為」,如「遷移」、「複製」等等,因此將我們的送貨員設定為常見的 Qt.MoveAction 即可。

若是其他沒有這種 items 的 QWidget,是不用設定此步驟的。

放下(BWidget:: dropEvent

終於到最後一步了,送貨員最終決定在此投遞,因此必須在此拆包,執行相關的函式。

為了方便,這裡與外界溝通的方式是使用信號槽將所有行號拆包發送出去,使用的是 self.dragIn 這個信號。

class BWidget(QTableWidget): def dropEvent(self, event): self.dragIn.emit(*[int(e) for e in event.mimeData().text().split(';')]) event.acceptProposedAction()

最後,還是要執行 acceptProposedAction 方法,才會關閉此事件。

之前介紹過如何接收外部拖入檔案,在程式中開啟的方法:

http://project.mde.tw/blog/40323230ri-zhi-1060116.html

這次挑戰較複雜的,在 Qt Widget 中抓起資訊的方式。

這三種 QTableWidget 的獨立原始碼提供在這裡,因為這其中的設定還透過繼承方式簡化函式,會較整齊些。

https://raw.githubusercontent.com/coursemdetw/project_site_files/gh-pages/files/pyslvs/dndExample.txt

可以透過對照的方式看看自己寫的是否完整。