I want to be able to click and drag over buttons and get a button-press-event for all of them.
from gi.repository import Gtk
class mainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Main")
self.tablex = 10
self.tabley = 10
self.resize(800,600)
scroll = Gtk.ScrolledWindow()
self.table = Gtk.Table(self.tablex, self.tabley, True)
align = Gtk.Alignment(xscale = 0,yscale = 0,xalign = 0,yalign = 0)
align.add(self.table)
scroll.add_with_viewport(align)
self.table.set_homogeneous(True)
self.fillTable()
self.add(scroll)
def fillTable(self):
self.tableList = []
for x in range(self.tablex):
self.tableList.append([])
for y in range(self.tabley):
self.tableList[x].append({"eBox" : Gtk.EventBox(), "image" : Gtk.Image(), "coords" : (x,y)})
self.tableList[x][y]["image"].set_from_stock(Gtk.STOCK_ADD, 5)
self.tableList[x][y]["eBox"].add(self.tableList[x][y]["image"])
self.tableList[x][y]["eBox"].connect("button-press-event", self.change, (x,y))
self.tableList[x][y]["eBox"].set_size_request(34,34)
self.table.attach(self.tableList[x][y]["eBox"],x,x+1,y,y+1)
def change(self,box, event, coords):
x = coords[0]
y = coords[1]
self.tableList[x][y]["image"].set_from_stock(Gtk.STOCK_APPLY, 5)
main = mainWindow()
main.connect("delete-event", Gtk.main_quit)
main.show_all()
Gtk.main()
like using:
self.tableList[x][y]["eBox"].connect("enter-notify-event", self.change, (x,y))
instead of:
self.tableList[x][y]["eBox"].connect("button-press-event", self.change, (x,y))
just only when I hold the mouse button down
Related
I want to draw some points at the mouse position (on the click of the mouse)
I use Python with Cairo
I've written some code but it doesn't work very well (I see the points when I click on the buttons).
I made the GUI using Glade which is also linked at the end of question.
import os,sys,platform
from os.path import join
import cairo
import gi
class Interface:
__gtype_name__ = "MonNom"
def __init__(self):
file_path = os.path.join('Interface.glade')
interface = Gtk.Builder()
interface.add_from_file(file_path)
self.double_buffer = None
interface.connect_signals(self)
window = interface.get_object("window")
window.show_all()
def on_draw(self, widget, cr):
if self.double_buffer is not None:
cr.set_source_surface(self.double_buffer, 0, 0)
cr.paint()
else:
print('Invalid double buffer')
return False
def on_configure(self, widget, event, data=None):
if self.double_buffer is not None:
self.double_buffer.finish()
self.double_buffer = None
self.double_buffer = cairo.ImageSurface(cairo.FORMAT_ARGB32, 600,400)
db = self.double_buffer
cc = cairo.Context(db)
cc.set_source_rgb(0,0, 0)
cc.rectangle(0, 0, 600, 400)
cc.set_source_rgb(1, 0, 0)
cc.stroke()
db.flush()
return False
def on_da_button_press(self, widget, event):
print ("Mouse clicked... at ", event.x, ", ", event.y)
# self.widget.queue_draw()
db = self.double_buffer
if db is not None:
cc = cairo.Context(db)
cc.move_to(event.x-5,event.y)
cc.line_to(event.x+5,event.y)
cc.move_to(event.x, event.y-5)
cc.line_to(event.x, event.y+5)
cc.set_source_rgb(0, 1, 0)
cc.stroke()
# db.flush()
return True
def on_destroy(self, widget):
Gtk.main_quit()
if __name__ == "__main__":
app = Interface()
Gtk.main()
The glade file
Is there an attribute to position subwindows in qmdiarea? I’m trying to center subwindow in middle of mainwindow on startup (mdiarea)
I’m working on a mcve but haven’t finished it, wanted to see if anyone has tried doing this, and how they did it
Subwindows are randomly placed on startup when initialized
class App(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent=parent)
self.setupUi(self)
self.screenShape = QDesktopWidget().screenGeometry()
self.width = self.screenShape.width()
self.height = self.screenShape.height()
self.resize(self.width * .6, self.height * .6)
self.new = []
#calls GUI's in other modules
self.lw = Login()
self.vs = VS()
self.ms = MS()
self.hw = HomeWindow()
self.mw = MainWindow()
self.ga = GA()
self.sGUI = Settings()
# shows subwindow
self.CreateLogin()
self.CreateVS()
self.CreateMS()
self.CreateGA()
self.CreateSettings()
def CreateLogin(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.lw)
self.subwindow.setAttribute(Qt.WA_DeleteOnClose, True)
self.mdiArea.addSubWindow(self.subwindow)
self.subwindow.setMaximumSize(520, 300)
self.subwindow.setMinimumSize(520, 300)
self.lw.showNormal()
def CreateVS(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.vs)
self.mdiArea.addSubWindow(self.subwindow)
self.vs.showMinimized()
def CreateMS(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.ms)
self.mdiArea.addSubWindow(self.subwindow)
self.ms.showMinimized()
self.ms.tabWidget.setCurrentIndex(0)
def CreateGA(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.ga)
self.mdiArea.addSubWindow(self.subwindow)
self.ga.showMinimized()
self.subwindow.setMaximumSize(820, 650)
def CreateSettings(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.sGUI)
self.mdiArea.addSubWindow(self.subwindow)
self.sGUI.showMinimized()
def CreateWindow(self):
self.hw.pushButton.clicked.connect(self.vs.showNormal)
self.hw.pushButton_2.clicked.connect(self.Moduleprogram)
self.hw.pushButton_3.clicked.connect(self.ms.showNormal)
self.hw.pushButton_4.clicked.connect(self.ga.showNormal)
self.subwindow = QMdiSubWindow()
self.subwindow.setWindowFlags(Qt.CustomizeWindowHint | Qt.Tool)
self.subwindow.setWidget(self.hw)
self.subwindow.setMaximumSize(258, 264)
self.subwindow.move(self.newwidth*.35, self.newheight*.25)
self.mdiArea.addSubWindow(self.subwindow)
In Qt the geometry is only effective when the window is visible so if you want to center something it must be in the showEvent method. On the other hand to center the QMdiSubWindow you must first get the center of the viewport of the QMdiArea, and according to that modify the geometry of the QMdiSubWindow.
Because the code you provide is complicated to execute, I have created my own code
from PyQt5 import QtCore, QtGui, QtWidgets
import random
def create_widget():
widget = QtWidgets.QLabel(
str(random.randint(0, 100)), alignment=QtCore.Qt.AlignCenter
)
widget.setStyleSheet(
"""background-color: {};""".format(
QtGui.QColor(*random.sample(range(255), 3)).name()
)
)
widget.setMinimumSize(*random.sample(range(100, 300), 2))
return widget
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
add_button = QtWidgets.QPushButton(
"Add subwindow", clicked=self.add_subwindow
)
self._mdiarea = QtWidgets.QMdiArea()
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
lay.addWidget(add_button)
lay.addWidget(self._mdiarea)
self._is_first_time = True
for _ in range(4):
self.add_subwindow()
#QtCore.pyqtSlot()
def add_subwindow(self):
widget = create_widget()
subwindow = QtWidgets.QMdiSubWindow(self._mdiarea)
subwindow.setWidget(widget)
subwindow.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
subwindow.show()
self._mdiarea.addSubWindow(subwindow)
# self.center_subwindow(subwindow)
def showEvent(self, event):
if self.isVisible() and self._is_first_time:
for subwindow in self._mdiarea.subWindowList():
self.center_subwindow(subwindow)
self._is_first_time = False
def center_subwindow(self, subwindow):
center = self._mdiarea.viewport().rect().center()
geo = subwindow.geometry()
geo.moveCenter(center)
subwindow.setGeometry(geo)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Update:
If you want the subwindow to be centered then with the following code you have to create a property center to True:
def add_subwindow(self):
widget = create_widget()
subwindow = QtWidgets.QMdiSubWindow(self._mdiarea)
subwindow.setWidget(widget)
subwindow.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
subwindow.show()
subwindow.setProperty("center", True) # <----
self._mdiarea.addSubWindow(subwindow)
def showEvent(self, event):
if self.isVisible() and self._is_first_time:
for subwindow in self._mdiarea.subWindowList():
if subwindow.property("center"): # <---
self.center_subwindow(subwindow)
self._is_first_time = False
What I want is an interface which opens windows under a SINGLE window, that is, I do NOT want the interface to open extra windows.
I will guide you to my error. When I run my code I get a home page, from there I click on View/Edit --> View/Edit Processed Slices. At this point this is what you should get in the MAIN WINDOW:
PICTURE 1
What I want the interface to do is to see the window in picture 2 whenever I click the blue rectangle. I want it to open the new widget IN THE SAME MAIN WINDOW
PICTURE 2
However, when I click it a new window opens and the previous window remains open (PICTURE 3). This is what I want to avoid, I want only 1 window not 2.
PICTURE 3
Here is the code:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import cv2
import numpy as np
"""
MAIN WINDOW
"""
class CancerSegWindow(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'Cancer Segmentation GUI '
self.initUI()
def initUI(self):
self.central = HOME()
self.setCentralWidget(self.central)
##
# ACTIONS
##
##File
#Exit
exitAct = QAction(QIcon('E:\BEATSON_PROJECT\python\GUI\exit.png'), 'Exit', self) # QAction is an abstraction for actions performed with a menubar, toolbar, or with a custom keyboard shortcut
exitAct.setShortcut('Ctrl+Q')
exitAct.setStatusTip('Exit application') # Status tip at the bottom
exitAct.triggered.connect(self.close) # Triggered signal is emitted. The signal is connected to the close() method of the QMainWindow widget.
#Home
HomeAct = QAction(QIcon('E:\BEATSON_PROJECT\python\GUI\home.png'), 'Home', self)
HomeAct.setStatusTip('Go Home')
HomeAct.triggered.connect(self.Home)
## View and Edit
# Slices
# Processed Slices
ProcessedAct = QAction('View / Edit Processed Slices', self)
ProcessedAct.triggered.connect(self.Processed_Slices)
self.statusBar().showMessage('Home') # First call statusBar creates a status bar at the bottom
# Subsequent calls return the statusbar object
##
# main MENU bar
##
menubar = self.menuBar() # create a menubar
# File
fileMenu = menubar.addMenu('File') # File (menu)
fileMenu.addAction(exitAct) # Exit
# View and Edit
veMenu = menubar.addMenu('View / Edit') # View and Edit (menu)
vesMenu = QMenu('View / Edit Slices',self) # Slices
vesMenu.addAction(ProcessedAct) # Processed
veMenu.addMenu(vesMenu)
##
# ICONS
##
toolbar = self.addToolBar('Exit')
toolbar.addAction(exitAct)
toolbarHome = self.addToolBar('Home')
toolbarHome.addAction(HomeAct)
##
# WINDOW
##
self.setGeometry(0, 30, 1366, 697)
self.setWindowTitle(self.title)
self.setWindowIcon(QIcon('E:\BEATSON_PROJECT\python\GUI\medicine.png'))
self.show()
def Home (self):
self.central = HOME()
self.setCentralWidget(self.central)
def Processed_Slices (self):
self.statusBar().showMessage('Displaying Processed Slices. Click on one Slice to View and Edit it individually')
self.central = ProcessedSlices()
self.setCentralWidget(self.central)
self.setWindowTitle(self.title + self.central.title)
def Pre_Edit_Processed (self, SliceNo=1):
self.statusBar().showMessage('Displaying Automatic Processed Slice' + str(SliceNo) + ' You can redraw it manually or modify the existing contour')
self.central = PreEditProcessed(SliceNo)
self.setCentralWidget(self.central)
self.setWindowTitle(self.title + self.central.title)
"""
HOME WINDOW
"""
class HOME (QWidget):
def __init__(self):
super().__init__()
#self.central = QPixmap("E:\BEATSON_PROJECT\python\GUI\Home_.png")
self.lbl1 = QLabel(self)
#self.lbl1.setPixmap(self.central)
"""
PROCESSED SLICES WINDOW
"""
class ProcessedSlices(QWidget):
def __init__(self):
super().__init__()
self.title = ('- Processed Slices')
self.initUI()
def initUI(self):
##
#CHECKBOXES
##
# Slice 1
#CheckBox
self.cb1 = QCheckBox('Slice 1', self)
self.cb1.move(1270, 115)
self.cb1.toggle()
self.cb1.stateChanged.connect(self.OpenSlice1)
#Pixmap (Image) 1
pixmap1 = QPixmap(310, 330) # Contour
pixmap1.fill(Qt.blue)
#pixmap1 = QPixmap("E:\BEATSON_PROJECT\python\GUI\Processed_Slice_1.png")
self.lbl1 = QLabel(self)
self.lbl1.setGeometry(QRect(QPoint(10,0),QPoint(310,330))) #
self.lbl1.setPixmap(pixmap1)
##
# SET GRID to obtain the mouse position
##
grid = QGridLayout()
self.text = "x: {0}, y: {1}".format(0, 0) # display the x and y coordinates of a mouse pointer in a label widget
self.label = QLabel(self.text, self) # x and y coordinates are displayd in a QLabel widget
grid.addWidget(self.label, 0, 1270, Qt.AlignTop)
self.setLayout(grid)
##
#WINDOW
##
#self.setGeometry(0, 25, 1365, 700)
#self.setWindowTitle('Processed Slices')
self.show()
def OpenSlice1(self, state):
self.lbl1.setVisible(state == Qt.Checked)
def mousePressEvent(self, e): # The e is the event object. it contains data about the event that was triggered
x = e.x() # in our case, a mouse CLICK
y = e.y() # x() and y() methods we determine the x and y coordinates of the mouse pointer
text = "None selected x: {0}, y: {1}"
if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
text = "Slice 1 x: {0}, y: {1}".format(x, y)
self.close()
self.CSW = CancerSegWindow()
self.CSW.Pre_Edit_Processed(1)
self.label.setText(text)
"""
PROCESSED SLICES CHECK WINDOW
"""
class PreEditProcessed(QWidget):
def __init__(self, SliceNo=1):
super().__init__()
self.title = ('- Check Processed Slices')
self._SliceNo = SliceNo
self.initUI()
def initUI(self):
#self.draw = Draw(self)
#self.draw._windows = 1
# Button to clear both image and drawing
self.button = QPushButton('Discard and segment MANUALLY ', self)
#self.button.clicked.connect(self.editManually)
# Button to modify contour
self.BmodContour = QPushButton('Modify existing contour ', self)
#self.BmodContour.clicked.connect(self.modContour)
# Button to finish and compare
self.BFinish = QPushButton('Finish ', self)
#self.BFinish.clicked.connect(self.Finish)
# Arrange Layout
self.layout = QVBoxLayout(self)
#self.layout.addWidget(self.draw) # Show Slice
self.layout.addWidget(self.button) # Manually
self.layout.addWidget(self.BmodContour) # Modify contour
self.layout.addWidget(self.BFinish) # Finish and compare
self.setGeometry(0, 25, 1365, 700)
self.setWindowTitle('Check Slices')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = CancerSegWindow()
sys.exit(app.exec_())
Note that the relevant part of the code is inside class ProcessedSlices:
def mousePressEvent(self, e):
x = e.x()
y = e.y()
text = "None selected x: {0}, y: {1}"
if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
text = "Slice 1 x: {0}, y: {1}".format(x, y)
self.close()
self.CSW = CancerSegWindow()
self.CSW.Pre_Edit_Processed(1)
Your problem is that you are creating another instance of the class CancerSegWindow() in the function mousePressEvent(self, e).
The best way is use pyqtSignal().
You have to declare the pyqtSignal(int) in ProcessedSlices class:
class ProcessedSlices(QWidget):
#here declare signal
signal = pyqtSignal(int)
def __init__(self):
# more code....
And emit the signal in your mousePressEvent(self, e):
def mousePressEvent(self, e): # The e is the event object. it contains data about the event that was triggered
x = e.x() # in our case, a mouse CLICK
y = e.y() # x() and y() methods we determine the x and y coordinates of the mouse pointer
text = "None selected x: {0}, y: {1}"
if ( x >= 10 and x <= 310 and y >= 0 and y <= 330 and self.cb1.isChecked()):
text = "Slice 1 x: {0}, y: {1}".format(x, y)
self.close()
self.signal.emit(1) # emit signal with SliceNo=1
self.label.setText(text)
Finally, capture it in your Processed_Slices():
def Processed_Slices (self):
self.statusBar().showMessage('Displaying Processed Slices. Click on one Slice to View and Edit it individually')
self.central = ProcessedSlices()
self.central.signal.connect(self.Pre_Edit_Processed) #connect signal
self.setCentralWidget(self.central)
self.setWindowTitle(self.title + self.central.title)
I have this simple Tkinter Custom Window. I am a beginner and only learnt tkinter a few months ago. I have no experience in real software development. So, I would like to know if the way it was coded is acceptable? I know that when i say acceptable , it could mean a lot of things. I just want to know what are the things i should improve in my coding style & the way i think?
import Tkinter as tk
''' Creating Tkinter Tk instance '''
class Application(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
self.bind("<ButtonPress-1>", self.StartMove)
self.bind("<ButtonRelease-1>", self.StopMove)
self.bind("<B1-Motion>", self.OnMotion)
self.Init()
self.Layout()
self.AddButtons()
''' Setting Main Tk window size & styles '''
def Init(self):
self.geometry("1280x700+0+0")
self.overrideredirect(True)
self['background'] = '#201F29'
self['highlightthickness'] = 2
self['relief'] = 'groove'
'''Layout of the Tk window'''
def Layout(self):
self.exitmenu = tk.Frame(self)
self.exitmenu.place(x = 1217, y = 0)
self.container = tk.Frame(self,width = 1268,height = 648 , relief = 'flat',bd = 0)
self.container.place(x = 5,y = 40)
''' Adding Exit button and Minimize button to the Tk window'''
def AddButtons(self):
self.minibutton = tk.Button(self.exitmenu,text = '0',font=('webdings',8,'bold'),relief = 'flat' , command = self.minimize )
self.minibutton.pack(side = 'left')
self.exitbutton = tk.Button(self.exitmenu,text = 'r',font=('webdings',8),relief = 'flat' ,bg = '#DB6B5A', command = self.destroy )
self.exitbutton.pack(side = 'left')
def minimize(self):
self.overrideredirect(False)
self.wm_state('iconic')
self.overrideredirect(True)
'''Methods for moving window frame'''
def StartMove(self, event):
self.x = event.x
self.y = event.y
def StopMove(self, event):
self.x = None
self.y = None
def OnMotion(self, event):
x1 = self.x
y1 = self.y
x2 = event.x
y2 = event.y
deltax = x2 - x1
deltay = y2 - y1
a = self.winfo_x() + deltax
b = self.winfo_y() + deltay
self.geometry("+%s+%s" % (a, b))
def Main():
app = Application()
app.mainloop()
if __name__ == "__main__":
Main()
Read PEP-8 Install and run one or all of PEP8 checker, pyFlakes, pyChecker, pylint.
The first thing that stands out is that docstrings are supposed to be within the function rather than before it - then they become a part of the function code and can be accessed by help.
How can I drag/move images using pygtk?
Here is a video of someone doing it, however the person did not give the source code.
here's a little demo (i don(t remmeber excatly were it comes from, sorry) which is very similar to the one found here: http://www.pygtk.org/pygtk2tutorial/examples/dragndrop.py
import gtk
class DragImage(gtk.Image):
def __init__(self,image,layout):
gtk.Image.__init__(self)
self.drag = False
self.drag_x = 0
self.drag_y = 0
self.layout = layout
self.x = 0
self.y = 0
self.set_from_file(image)
self.event_box = gtk.EventBox()
self.event_box.set_visible_window(False)
self.event_box.add(self)
self.event_box.add_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
self.event_box.connect("button-press-event", self.click)
self.event_box.connect("button-release-event", self.release)
self.event_box.connect("motion-notify-event", self.mousemove)
self.layout.put( self.event_box, 0, 0 )
def click(self, widget, event):
self.drag = True
self.drag_x = event.x
self.drag_y = event.y
print(self.drag_x, self.drag_y)
def release(self, widget, event):
self.drag = False
def mousemove(self,widget,event):
if self.drag:
self.layout.move(self.event_box,self.x+int(event.x-self.drag_x),self.y+int(event.y-self.drag_y))
self.x, self.y = self.layout.child_get(self.event_box,'x','y')
class move_test(object):
def __init__(self):
window = gtk.Window()
layout = gtk.Layout()
img1 = DragImage('image1.jpg',layout)
img2 = DragImage('image2.jpg',layout)
window.add(layout)
window.show_all()
move_test()
gtk.main()