Related
I have made a custom widget similar to QPushbutton or label. I would like to let the user resize the widget when the mouse is over the edge of the widget. How can I do this?
(Note: I am not looking for Splitter window)
An image editing software, you have a dedicated "space" for the image, and the user is free to do anything she/he wants within the boundaries of that space. When a widget is placed within a layout-managed container (as it normally should) that can represent multiple issues. Not only you've to implement the whole mouse interaction to resize the widget, but you also need to notify the possible parent widget(s) about the resizing.
That said, what you're trying to achieve can be done, with some caveats.
The following is a very basic implementation of a standard QWidget that is able to resize itself, while notifying its parent widget(s) about the size hint modifications. Note that this is not complete, and its behavior doesn't correctly respond to mouse movements whenever they happen on the top or left edges of the widget. Moreover, while it (could) correctly resize the parent widget(s) while increasing its size, the resize doesn't happen when shrinking. This can theoretically be achieved by setting a minimumSize() and manually calling adjustSize() but, in order to correctly provide all the possible features required by a similar concept, you'll need to do the whole implementation by yourself.
from PyQt5 import QtCore, QtGui, QtWidgets
Left, Right = 1, 2
Top, Bottom = 4, 8
TopLeft = Top|Left
TopRight = Top|Right
BottomRight = Bottom|Right
BottomLeft = Bottom|Left
class ResizableLabel(QtWidgets.QWidget):
resizeMargin = 4
# note that the Left, Top, Right, Bottom constants cannot be used as class
# attributes if you want to use list comprehension for better performance,
# and that's due to the variable scope behavior on Python 3
sections = [x|y for x in (Left, Right) for y in (Top, Bottom)]
cursors = {
Left: QtCore.Qt.SizeHorCursor,
Top|Left: QtCore.Qt.SizeFDiagCursor,
Top: QtCore.Qt.SizeVerCursor,
Top|Right: QtCore.Qt.SizeBDiagCursor,
Right: QtCore.Qt.SizeHorCursor,
Bottom|Right: QtCore.Qt.SizeFDiagCursor,
Bottom: QtCore.Qt.SizeVerCursor,
Bottom|Left: QtCore.Qt.SizeBDiagCursor,
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.startPos = self.section = None
self.rects = {section:QtCore.QRect() for section in self.sections}
# mandatory for cursor updates
self.setMouseTracking(True)
# just for demonstration purposes
background = QtGui.QPixmap(3, 3)
background.fill(QtCore.Qt.transparent)
qp = QtGui.QPainter(background)
pen = QtGui.QPen(QtCore.Qt.darkGray, .5)
qp.setPen(pen)
qp.drawLine(0, 2, 2, 0)
qp.end()
self.background = QtGui.QBrush(background)
def updateCursor(self, pos):
for section, rect in self.rects.items():
if pos in rect:
self.setCursor(self.cursors[section])
self.section = section
return section
self.unsetCursor()
def adjustSize(self):
del self._sizeHint
super().adjustSize()
def minimumSizeHint(self):
try:
return self._sizeHint
except:
return super().minimumSizeHint()
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
if self.updateCursor(event.pos()):
self.startPos = event.pos()
return
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.startPos is not None:
delta = event.pos() - self.startPos
if self.section & Left:
delta.setX(-delta.x())
elif not self.section & (Left|Right):
delta.setX(0)
if self.section & Top:
delta.setY(-delta.y())
elif not self.section & (Top|Bottom):
delta.setY(0)
newSize = QtCore.QSize(self.width() + delta.x(), self.height() + delta.y())
self._sizeHint = newSize
self.startPos = event.pos()
self.updateGeometry()
elif not event.buttons():
self.updateCursor(event.pos())
super().mouseMoveEvent(event)
self.update()
def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
self.updateCursor(event.pos())
self.startPos = self.section = None
self.setMinimumSize(0, 0)
def resizeEvent(self, event):
super().resizeEvent(event)
outRect = self.rect()
inRect = self.rect().adjusted(self.resizeMargin, self.resizeMargin, -self.resizeMargin, -self.resizeMargin)
self.rects[Left] = QtCore.QRect(outRect.left(), inRect.top(), self.resizeMargin, inRect.height())
self.rects[TopLeft] = QtCore.QRect(outRect.topLeft(), inRect.topLeft())
self.rects[Top] = QtCore.QRect(inRect.left(), outRect.top(), inRect.width(), self.resizeMargin)
self.rects[TopRight] = QtCore.QRect(inRect.right(), outRect.top(), self.resizeMargin, self.resizeMargin)
self.rects[Right] = QtCore.QRect(inRect.right(), self.resizeMargin, self.resizeMargin, inRect.height())
self.rects[BottomRight] = QtCore.QRect(inRect.bottomRight(), outRect.bottomRight())
self.rects[Bottom] = QtCore.QRect(inRect.left(), inRect.bottom(), inRect.width(), self.resizeMargin)
self.rects[BottomLeft] = QtCore.QRect(outRect.bottomLeft(), inRect.bottomLeft()).normalized()
# ---- optional, mostly for demonstration purposes ----
def paintEvent(self, event):
super().paintEvent(event)
qp = QtGui.QPainter(self)
if self.underMouse() and self.section:
qp.save()
qp.setPen(QtCore.Qt.lightGray)
qp.setBrush(self.background)
qp.drawRect(self.rect().adjusted(0, 0, -1, -1))
qp.restore()
qp.drawText(self.rect(), QtCore.Qt.AlignCenter, '{}x{}'.format(self.width(), self.height()))
def enterEvent(self, event):
self.update()
def leaveEvent(self, event):
self.update()
class Test(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QGridLayout(self)
for row in range(3):
for column in range(3):
if (row, column) == (1, 1):
continue
layout.addWidget(QtWidgets.QPushButton(), row, column)
label = ResizableLabel()
layout.addWidget(label, 1, 1)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Test()
w.show()
sys.exit(app.exec_())
I'm facing a problem with python.
i want to draw on my monitor a circle, that can move around.
let's say i have my browser open, i want to be able to make the circle go around on his own AND be able to use the mouse to press any button i want.
the idea is that the circle is connected to my hands movement thanks to Leap Motion, and i want to display the gestures i make while being able to use the mouse.
my worries are that i have to make a trasparent window which doesn't let me to use the mouse because i would clik on the trasparent window.
Thanks!
Found the solution, i used wxPython
Down below the code:
import wx, inspect, os, sys, win32gui
IMAGE_PATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + '\cerchio.png'
class Cursor(wx.Frame):
def __init__(self, parent, log):
self.log = log
self.delta = wx.Point(0,0)
wx.Frame.__init__(self, parent, -1, "Shaped Window",
style =
wx.FRAME_SHAPED
| wx.SIMPLE_BORDER
| wx.FRAME_NO_TASKBAR
| wx.STAY_ON_TOP
)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.update, self.timer)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.x=0
self.y=0
self.hasShape = False
self.SetClientSize( (50, 50) )
image = wx.Image(IMAGE_PATH, wx.BITMAP_TYPE_PNG)
image.SetMaskColour(255,255,255)
image.SetMask(True)
self.bmp = wx.BitmapFromImage(image)
self.SetWindowShape()
self.timer.Start(1)
dc = wx.ClientDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
def OnExit(self, evt):
self.Close()
def SetWindowShape(self, *evt):
# Use the bitmap's mask to determine the region
r = wx.RegionFromBitmap(self.bmp)
self.hasShape = self.SetShape(r)
def OnPaint(self, evt):
dc = wx.PaintDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
def OnExit(self, evt):
self.Close()
def update(self, event):
#self.x, self.y = win32gui.GetCursorPos()
self.SetPosition((self.x,self.y))
if __name__ == '__main__':
app = wx.App( False )
frm = Cursor(None, -1)
frm.Show()
app.MainLoop()
This maps 50x50 png image with white background (modify IMAGE_PATH) to the cursor position (you won't be able to click though)
This code is from python-forum and it's there to view images in a simple slideshow. It doesn't work I think because it creates the images but doesn't show them on the screen. I think i might have to implement some sort of callback but is that correct way to solve my problem? If i get rid of the while loop and just use the for it will only show one image and the not the rest.
class SecondFrame(wx.Frame):
def __init__(self, parent, mysize):
gets correct path for images from working directory
wx.EVT_PAINT(self, self.onPaint)
def onPaint(self, event=None):
dc = wx.PaintDC(self)
while self.loops > 0:
self.loops -= 1
for self.ix, bmp in enumerate(self.image_list):
w, h = bmp.GetSize()
info = "Graphs"
self.SetTitle(info)
dc.DrawBitmap(bmp, 10, 10, True)
wx.MilliSleep(self.delay)
if self.delay > 200:
dc.Clear()
I am not exactly sure where you got that code, but as it stands it is incomplete, non-runnable and very poorly coded overall (IMHO). Try this snippet (untested):
class SecondFrame(wx.Frame):
def __init__(self, parent, mysize):
# Whatever here
self.timer = wx.Timer(self, wx.ID_ANY)
self.SetTitle("Graphs")
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnErase)
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.timer.Start(self.delay)
def OnPaint(self, event):
dc = wx.BufferedPaintDC(self)
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
if self.loops <= 0:
return
bmp = self.image_list[self.loops]
w, h = bmp.GetSize()
dc.DrawBitmap(bmp, 10, 10, True)
wx.MilliSleep(self.delay)
def OnErase(self, event):
pass
def OnTimer(self, event):
self.loops -= 1
if self.loops <= 0:
return
self.Refresh()
The next time, please post a complete, short, runnable and standalone sample app as per wxPython guidelines:
http://wiki.wxpython.org/MakingSampleApps
I am trying to write a non-webbased client for a chat service on a web site, it connects to it fine via socket and can communicate with it and everything. I am writing a GUI for it (I tried writing it in tkinter but I hit some walls I really wantd to get passed, so I switched to wxPython)
What I'm having a problem with:
This application uses an extended Notebook widget called NotebookCtrl. However the same problem appears with regular Notebook. First it creates a page in which things are logged to, which is successful, and then it connects, and it's supposed to add pages with each chatroom it joins on the service. However, when it adds a tab after it's mainloop has been started (I am communicating with the GUI and the sockets via queues and threading), the tab comes up completely blank. I have been stuck on this for hours and hours and got absolutely nowhere
The example that came with the NotebookCtrl download adds and deletes pages by itself perfectly fine. I am on the edge of completely giving up on this project. Here is what the code looks like (note this is a very small portion of the application, but this covers the wxPython stuff)
class Chatroom(Panel):
''' Frame for the notebook widget to tabulate a chatroom'''
def __init__(self, ns, parent):
Panel.__init__(self, parent, -1)
self.msgs, self.typed, self.pcbuff = [], [], {}
self.members, self._topic, self._title, self.pc = None, None, None, None
self.name, self.tabsign, = ns, 0
self.hSizer1 = wx.BoxSizer(wx.HORIZONTAL)
self.vSizer = wx.BoxSizer(wx.VERTICAL)
self.Grid = wx.GridBagSizer(5, 2)
self.Title = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE | wx.TE_READONLY)
self.Topic = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE | wx.TE_READONLY)
self.Privclasses = TreeCtrl(self, size=(150, -1))
self.Buffer = wx.html.HtmlWindow(self)
self.Buffer.SetStandardFonts(8)
self.templbl = StaticText(self, -1, 'This is where the formatting buttons will go!')
# note to remember: self.templbl.SetLabel('string') sets the label
self.Typer = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE)
self.Grid.Add(self.Title, (0,0), (1,2), wx.EXPAND, 2)
self.Grid.Add(self.Topic, (1,0), (1,1), wx.EXPAND, 2)
self.Grid.Add(self.Privclasses, (1,1), (2,1), wx.EXPAND, 2)
self.Grid.Add(self.Buffer, (2,0), (1,1), wx.EXPAND, 2)
self.Grid.Add(self.templbl, (3,0), (1,1), wx.EXPAND | wx.ALIGN_LEFT, 2)
self.Grid.Add(self.Typer, (4,0), (1,1), wx.EXPAND, 2)
self.Grid.AddGrowableCol(0)
self.Grid.AddGrowableRow(2)
self.SetSizerAndFit(self.Grid)
self.Show(True)
self.Typer.Bind(EVT_CHAR, self.Typer_OnKeyDown)
def Typer_OnKeyDown(self, event):
keycode = event.GetKeyCode()
if event.ShiftDown():
if keycode == WXK_RETURN:
pass
elif keycode == WXK_BACK:
pass
elif keycode == WXK_UP:
pass
elif keycode == WXK_DOWN:
pass
else:
if keycode == WXK_RETURN:
pass
event.Skip()
def Write(self, msg, K):
self.msgs.append(msg)
if len(self.msgs) > 300:
self.msgs = self.msgs[50:]
self.Buffer.SetPage('<br>'.join(self.msgs))
class Application(App):
def __init__(self, K):
self.Queue = Queue.Queue()
self.current = ''
self.chatorder = []
self.Window = App(0)
self.frame = MainFrame(None, 0, "Komodo Dragon")
self.Pages = NC.NotebookCtrl(self.frame, 9000)
self.Channels = {}
self.AddChatroom('~Komodo', K)
self.frame.Show(True)
self.Window.SetTopWindow(self.frame)
self.Timer = _Timer(0.050, self.OnTimer)
self.Timer.start()
self.Pages.Bind(NC.EVT_NOTEBOOKCTRL_PAGE_CHANGED, self.onPageChanged)
self.Pages.Bind(NC.EVT_NOTEBOOKCTRL_PAGE_CHANGING, self.onPageChanging)
self.Pages.Bind(EVT_PAINT, self.onPaint)
self.Pages.Bind(EVT_SIZE, self.onSize)
def onPaint(self, event):
event.Skip()
def onSize(self, event):
event.Skip()
def Run(self):
self.Window.MainLoop()
def onPageChanged(self, event):
event.Skip()
def onPageChanging(self, event):
event.Skip()
# Timer and Queue functions
def OnTimer(self):
self.CheckQueue()
self.Timer = _Timer(0.050, self.OnTimer)
self.Timer.start()
def CheckQueue(self): # the Application needs to use a queue to do things in order to prevent
try: # overlaps from happening, such as runtime errors and widgets changing
while not self.Queue.empty(): # suddenly. The most common error seems to be size
func = self.Queue.get_nowait() # changes during iterations. Everything from
func() # packet processing to adding widgets needs to wait in line this way
except Queue.Empty:
pass
def AddQueue(self, func):
self.Queue.put(func)
# Channel controls
def AddChatroom(self, ns, K):
if ns in self.Channels: return
#self.typedindex = 0
c = K.format_ns(ns)
self.chatorder.append(ns)
self.current = ns
self.Channels[ns] = Chatroom(ns, self.Pages)
self.Pages.AddPage(self.Channels[ns], ns, True)
def DeleteChatroom(self, ns, bot): # Delete a channel, it's properties, and buttons
ind = self.chatorder.index(ns)
del self.chatorder[ind]
for each in self.chatorder[ind:]:
x = self.channels[each].tab.grid_info()
if x['column'] == '0': r, c = int(x['row'])-1, 9
else: r, c = int(x['row']), int(x['column'])-1
self.channels[each].tab.grid_configure(row=r, column=c)
x = self.channels[each].tab.grid_info()
self.channels[ns].Tab.destroy()
self.channels[ns].tab.destroy()
self.channels[self.chatorder[-1]].tab.select()
self.switchtab(bot, self.chatorder[-1])
x = self.tabids_reverse[ns]
del self.tabids_reverse[ns], self.tabids[x], self.channels[ns]
The Chatroom class covers what each tab should have in it. the first tab that is added in class Application's init function is perfectly fine, and even prints messages it receives from the chat service when it attempts to connect. Once the service sends a join packet to me, it calls the same exact function used to add that tab, AddChatroom(), and should create the exact same thing, only printing messages specifically for that chatroom. It creates the tab, but the Chatroom page is completely empty and grey. I am very sad :C
Thanks in advance if you can help me.
EDIT
I have written a working example of this script you can run yourself (if you have wxPython). It uses regular Notebook instead of NotebookCtrl so you don't have to download that widget as well, but the bug happens for both. The example sets up the window, and sets up the main debug tab and then a chatroom tab before entering mainloop. After mainloop, any chatrooms added afterwords are completely blank
from wx import *
import Queue, time
from threading import Timer as _Timer
from threading import Thread
# import System._NotebookCtrl.NotebookCtrl as NC ## Using regular notebook for this example
class MainFrame(Frame):
def __init__(self, parent, ID, title):
ID_FILE_LOGIN = 100
ID_FILE_RESTART = 101
ID_FILE_EXIT = 102
ID_HELP_ABOUT = 200
Frame.__init__(self, parent, ID, title,
DefaultPosition, Size(1000, 600))
self.CreateStatusBar()
self.SetStatusText("This is the statusbar")
menu_File = Menu()
menu_Help = Menu()
menu_Edit = Menu()
menu_Config = Menu()
menu_File.Append(ID_FILE_LOGIN, "&Login Info",
"Enter your deviantArt Login information")
menu_File.AppendSeparator()
menu_File.Append(ID_FILE_RESTART, "&Restart",
"Restart the program")
menu_File.Append(ID_FILE_EXIT, "E&xit",
"Terminate the program")
menu_Help.Append(ID_HELP_ABOUT, "&About",
"More information about this program")
menuBar = MenuBar()
menuBar.Append(menu_File, "&File");
menuBar.Append(menu_Edit, "&Edit");
menuBar.Append(menu_Config, "&Config");
menuBar.Append(menu_Help, "&Help");
self.SetMenuBar(menuBar)
EVT_MENU(self, ID_FILE_LOGIN, self.OnLogin)
EVT_MENU(self, ID_FILE_RESTART, self.OnRestart)
EVT_MENU(self, ID_FILE_EXIT, self.OnQuit)
EVT_MENU(self, ID_HELP_ABOUT, self.OnAbout)
def OnAbout(self, event):
dlg = MessageDialog(self, "Hi! I am Komodo Dragon! I am an application\n"
"that communicates with deviantArt's Messaging Network (dAmn)",
"Komodo", OK | ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def OnQuit(self, event):
self.Close(True)
def OnRestart(self, event):
pass
def OnLogin(self, event):
dlg = MessageDialog(self, "Enter your Login information here:\n"
"Work in progress, LOL",
"Login", OK | ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
class Chatroom(Panel):
''' Frame for the notebook widget to tabulate a chatroom'''
def __init__(self, parent, ns):
Panel.__init__(self, parent, -1)
self.msgs, self.typed, self.pcbuff = [], [], {}
self.members, self._topic, self._title, self.pc = None, None, None, None
self.name, self.tabsign, = ns, 0
self.hSizer1 = wx.BoxSizer(wx.HORIZONTAL)
self.vSizer = wx.BoxSizer(wx.VERTICAL)
self.Grid = wx.GridBagSizer(5, 2)
self.Title = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE | wx.TE_READONLY)
self.Topic = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE | wx.TE_READONLY)
self.Privclasses = TreeCtrl(self, size=(150, -1))
self.Buffer = wx.html.HtmlWindow(self)
self.Buffer.SetStandardFonts(8)
self.templbl = StaticText(self, -1, 'This is where the formatting buttons will go!')
self.Typer = TextCtrl(self, size=(-1, 50), style=wx.TE_MULTILINE)
self.Grid.Add(self.Title, (0,0), (1,2), wx.EXPAND, 2)
self.Grid.Add(self.Topic, (1,0), (1,1), wx.EXPAND, 2)
self.Grid.Add(self.Privclasses, (1,1), (2,1), wx.EXPAND, 2)
self.Grid.Add(self.Buffer, (2,0), (1,1), wx.EXPAND, 2)
self.Grid.Add(self.templbl, (3,0), (1,1), wx.EXPAND | wx.ALIGN_LEFT, 2)
self.Grid.Add(self.Typer, (4,0), (1,1), wx.EXPAND, 2)
self.Grid.AddGrowableCol(0)
self.Grid.AddGrowableRow(2)
self.SetSizerAndFit(self.Grid)
self.Show(True)
self.Typer.Bind(EVT_CHAR, self.Typer_OnKeyDown)
def Typer_OnKeyDown(self, event):
keycode = event.GetKeyCode()
if event.ShiftDown():
if keycode == WXK_RETURN:
pass
elif keycode == WXK_BACK:
pass
elif keycode == WXK_UP:
pass
elif keycode == WXK_DOWN:
pass
else:
if keycode == WXK_RETURN:
pass
event.Skip()
def Write(self, msg):
self.msgs.append(msg)
if len(self.msgs) > 300:
self.msgs = self.msgs[50:]
self.Buffer.SetPage('<br>'.join(self.msgs))
class Application(App):
def __init__(self, K):
self.Queue = Queue.Queue()
self.current = ''
self.chatorder = []
self.Window = App(0)
self.frame = MainFrame(None, 0, "Komodo Dragon")
self.Pages = Notebook(self.frame, 9000)
self.Channels = {}
self.AddChatroom('~Komodo', K)
self.frame.Show(True)
self.Window.SetTopWindow(self.frame)
self.Timer = _Timer(0.050, self.OnTimer)
self.Pages.Bind(EVT_NOTEBOOK_PAGE_CHANGED, self.onPageChanged)
self.Pages.Bind(EVT_NOTEBOOK_PAGE_CHANGING, self.onPageChanging)
self.Pages.Bind(EVT_PAINT, self.onPaint)
self.Pages.Bind(EVT_SIZE, self.onSize)
def onPaint(self, event):
event.Skip()
def onSize(self, event):
event.Skip()
def onPageChanged(self, event):
event.Skip()
def onPageChanging(self, event):
event.Skip()
def Run(self):
self.Window.MainLoop()
# Timer and Queue functions
def OnTimer(self):
self.CheckQueue()
self.Timer = _Timer(0.050, self.OnTimer)
self.Timer.start()
def CheckQueue(self): # the Application needs to use a queue to do things in order to prevent
try: # overlaps from happening, such as runtime errors and widgets changing
while not self.Queue.empty(): # suddenly. The most common error seems to be size
func = self.Queue.get_nowait() # changes during iterations. Everything from
if func is not None:
func() # packet processing to adding widgets needs to wait in line this way
except Queue.Empty:
pass
def AddQueue(self, func):
self.Queue.put(func)
# Channel controls
def AddChatroom(self, ns, K):
if ns in self.Channels: return
self.chatorder.append(ns)
self.current = ns
self.Channels[ns] = Chatroom(self.Pages, ns)
self.Pages.AddPage(self.Channels[ns], ns, True)
class _Thread(Thread):
def __init__(self, K):
Thread.__init__(self)
self.K = K
def run(self):
self.K.Mainloop()
class K:
def __init__(self):
self.App = Application(self)
self.App.AddQueue(self.App.Channels['~Komodo'].Write('>> Welcome!') )
self.App.AddQueue(self.App.Channels['~Komodo'].Write('>> Entering mainloop...') )
self.App.AddChatroom('#TestChatroom1', self)
self.roomcount = 1
self.timer = time.time() + 3
self.thread = _Thread(self)
self.thread.start()
self.App.Timer.start()
self.App.Run()
def Mainloop(self):
while True:
if time.time() > self.timer:
self.App.AddQueue(
lambda: self.App.Channels['~Komodo'].Write('>> Testing') )
if self.roomcount < 5:
self.roomcount += 1
self.App.AddQueue(
lambda: self.App.AddChatroom('#TestChatroom{0}'.format(self.roomcount), self) )
self.timer = time.time() + 3
if __name__ == '__main__':
test = K()
Here is your problem:
lambda: self.App.AddChatroom('#TestChatroom{0}'.format(self.roomcount), self) )
Fixed by using wx.CallAfter (tested on win xp sp3):
lambda: wx.CallAfter(self.App.AddChatroom, '#TestChatroom{0}'.format(self.roomcount), self)
You were probably tying up the GUI by calling wx objects from thread code. See this wxPython wiki article.
It doesn't look like you're creating your Chatroom with its parent as the Notebook. What is "K" in Application.__init__? (you didn't post a fully runnable sample)
When adding or deleting tabs, you probably need to call Layout() right after you're done. One easy way to find out is to grab an edge of the frame and resize it slightly. If you see widgets magically appear, then it's because Layout() was called and your page re-drawn.
I have been working on this project for some time now - it was originally supposed to be a test to see if, using wxPython, I could build a button 'from scratch.' From scratch means: that i would have full control over all the aspects of the button (i.e. controlling the BMP's that are displayed... what the event handlers did... etc.)
I have run into several problems (as this is my first major python project.) Now, when the all the code is working for the life of me I can't get an image to display.
Basic code - not working
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"), 100, 100)
self.Refresh()
self.Update()
Full Main.py
import wx
from Custom_Button import Custom_Button
from wxPython.wx import *
ID_ABOUT = 101
ID_EXIT = 102
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wxFrame.__init__(self, parent, ID, title,
wxDefaultPosition, wxSize(400, 400))
self.CreateStatusBar()
self.SetStatusText("Program testing custom button overlays")
menu = wxMenu()
menu.Append(ID_ABOUT, "&About", "More information about this program")
menu.AppendSeparator()
menu.Append(ID_EXIT, "E&xit", "Terminate the program")
menuBar = wxMenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
# The call for the 'Experiential button'
self.Button1 = Custom_Button(parent, -1,
wx.Point(100, 100),
wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"),
wx.Bitmap("/home/wallter/Desktop/Normal.bmp"),
wx.Bitmap("/home/wallter/Desktop/Click.bmp"))
# The following three lines of code are in place to try to get the
# Button1 to display (trying to trigger the Paint event (the _onPaint.)
# Because that is where the 'draw' functions are.
self.Button1.Show(true)
self.Refresh()
self.Update()
# Because the Above three lines of code did not work, I added the
# following four lines to trigger the 'draw' functions to test if the
# '_onPaint' method actually worked.
# These lines do not work.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.DrawBitmap(wx.Bitmap("/home/wallter/Desktop/Mouseover.bmp"), 100, 100)
EVT_MENU(self, ID_ABOUT, self.OnAbout)
EVT_MENU(self, ID_EXIT, self.TimeToQuit)
def OnAbout(self, event):
dlg = wxMessageDialog(self, "Testing the functions of custom "
"buttons using pyDev and wxPython",
"About", wxOK | wxICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def TimeToQuit(self, event):
self.Close(true)
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(NULL, -1, "wxPython | Buttons")
frame.Show(true)
self.SetTopWindow(frame)
return true
app = MyApp(0)
app.MainLoop()
Full CustomButton.py
import wx
from wxPython.wx import *
class Custom_Button(wx.PyControl):
def __init__(self, parent, id, Pos, Over_BMP, Norm_BMP, Push_BMP, **kwargs):
wx.PyControl.__init__(self,parent, id, **kwargs)
self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
self.Bind(wx.EVT_LEFT_UP, self._onMouseUp)
self.Bind(wx.EVT_LEAVE_WINDOW, self._onMouseLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self._onMouseEnter)
self.Bind(wx.EVT_ERASE_BACKGROUND,self._onEraseBackground)
self.Bind(wx.EVT_PAINT,self._onPaint)
self.pos = Pos
self.Over_bmp = Over_BMP
self.Norm_bmp = Norm_BMP
self.Push_bmp = Push_BMP
self._mouseIn = False
self._mouseDown = False
def _onMouseEnter(self, event):
self._mouseIn = True
def _onMouseLeave(self, event):
self._mouseIn = False
def _onMouseDown(self, event):
self._mouseDown = True
def _onMouseUp(self, event):
self._mouseDown = False
self.sendButtonEvent()
def sendButtonEvent(self):
event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
event.SetInt(0)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
def _onEraseBackground(self,event):
# reduce flicker
pass
def Iz(self):
dc = wx.BufferedPaintDC(self)
dc.DrawBitmap(self.Norm_bmp, 100, 100)
def _onPaint(self, event):
# The printing functions, they should work... but don't.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self.Norm_bmp)
# This never printed... I don't know if that means if the EVT
# is triggering or what.
print '_onPaint'
# draw whatever you want to draw
# draw glossy bitmaps e.g. dc.DrawBitmap
if self._mouseIn: # If the Mouse is over the button
dc.DrawBitmap(self.Over_bmp, self.pos)
else: # Since the mouse isn't over it Print the normal one
# This is adding on the above code to draw the bmp
# in an attempt to get the bmp to display; to no avail.
dc.DrawBitmap(self.Norm_bmp, self.pos)
if self._mouseDown: # If the Mouse clicks the button
dc.DrawBitmap(self.Push_bmp, self.pos)
This code won't work? I get no BMP displayed why? How do i get one? I've gotten the staticBitmap(...) to display one, but it won't move, resize, or anything for that matter... - it's only in the top left corner of the frame?
Note: the frame is 400pxl X 400pxl - and the "/home/wallter/Desktop/Mouseover.bmp"
Are your sure you code is working without exceptions because when I run it i get many errors, read the points below and you should have a button which at least draws correctly
When O run it it gives error because Custom_Button is passed NULL parent instead pass frame e.g. Custom_Button(self, ...)
Your drawBitmap call is also wrong, it throws exception, instead of dc.DrawBitmap(self.Norm_bmp) it should be dc.DrawBitmap(self.Norm_bmp, 0, 0)
dc.DrawBitmap(self.Over_bmp, self.pos) also throws error as pos should be x,y not a tuple so instead do dc.DrawBitmap(self.Over_bmp, *self.pos)
and lastly you do not need to do "from wxPython.wx import *" instead just do "from wx import *" and instead of wxXXX class names use wx.XXX, instead of true use True etc
here is my working code
from wx import *
ID_ABOUT = 101
ID_EXIT = 102
class Custom_Button(wx.PyControl):
def __init__(self, parent, id, Pos, Over_BMP, Norm_BMP, Push_BMP, **kwargs):
wx.PyControl.__init__(self,parent, id, **kwargs)
self.Bind(wx.EVT_LEFT_DOWN, self._onMouseDown)
self.Bind(wx.EVT_LEFT_UP, self._onMouseUp)
self.Bind(wx.EVT_LEAVE_WINDOW, self._onMouseLeave)
self.Bind(wx.EVT_ENTER_WINDOW, self._onMouseEnter)
self.Bind(wx.EVT_ERASE_BACKGROUND,self._onEraseBackground)
self.Bind(wx.EVT_PAINT,self._onPaint)
self.pos = Pos
self.Over_bmp = Over_BMP
self.Norm_bmp = Norm_BMP
self.Push_bmp = Push_BMP
self._mouseIn = False
self._mouseDown = False
def _onMouseEnter(self, event):
self._mouseIn = True
def _onMouseLeave(self, event):
self._mouseIn = False
def _onMouseDown(self, event):
self._mouseDown = True
def _onMouseUp(self, event):
self._mouseDown = False
self.sendButtonEvent()
def sendButtonEvent(self):
event = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, self.GetId())
event.SetInt(0)
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)
def _onEraseBackground(self,event):
# reduce flicker
pass
def Iz(self):
dc = wx.BufferedPaintDC(self)
dc.DrawBitmap(self.Norm_bmp, 100, 100)
def _onPaint(self, event):
# The printing functions, they should work... but don't.
dc = wx.BufferedPaintDC(self)
dc.SetFont(self.GetFont())
dc.SetBackground(wx.Brush(self.GetBackgroundColour()))
dc.Clear()
dc.DrawBitmap(self.Norm_bmp, 0, 0)
# This never printed... I don't know if that means if the EVT
# is triggering or what.
print '_onPaint'
# draw whatever you want to draw
# draw glossy bitmaps e.g. dc.DrawBitmap
if self._mouseIn: # If the Mouse is over the button
dc.DrawBitmap(self.Over_bmp, *self.pos)
else: # Since the mouse isn't over it Print the normal one
# This is adding on the above code to draw the bmp
# in an attempt to get the bmp to display; to no avail.
dc.DrawBitmap(self.Norm_bmp, *self.pos)
if self._mouseDown: # If the Mouse clicks the button
dc.DrawBitmap(self.Push_bmp, *self.pos)
class MyFrame(wx.Frame):
def __init__(self, parent, ID, title):
wx.Frame.__init__(self, parent, ID, title,
wx.DefaultPosition, wx.Size(400, 400))
self.CreateStatusBar()
self.SetStatusText("Program testing custom button overlays")
menu = wx.Menu()
menu.Append(ID_ABOUT, "&About", "More information about this program")
menu.AppendSeparator()
menu.Append(ID_EXIT, "E&xit", "Terminate the program")
menuBar = wx.MenuBar()
menuBar.Append(menu, "&File");
self.SetMenuBar(menuBar)
# The call for the 'Experiential button'
s = r"D:\virtual_pc\mockup\mockupscreens\embed_images\toolbar\options.png"
self.Button1 = Custom_Button(self, -1,
wx.Point(100, 100),
wx.Bitmap(s),
wx.Bitmap(s),
wx.Bitmap(s))
self.Button1.Show(True)
EVT_MENU(self, ID_ABOUT, self.OnAbout)
EVT_MENU(self, ID_EXIT, self.TimeToQuit)
def OnAbout(self, event):
dlg = wxMessageDialog(self, "Testing the functions of custom "
"buttons using pyDev and wxPython",
"About", wxOK | wxICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def TimeToQuit(self, event):
self.Close(true)
class MyApp(wx.App):
def OnInit(self):
frame = MyFrame(None, -1, "wxPython | Buttons")
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()