Greetings;
I'm having a bit of trouble correctly instantiating an Array of System.Drawing.Point instances, and then adding the Array of Points to a GDI+ GraphicsPath instance using IronPython in a WinForms application. The following code compiles or builds correctly under SharpDevelop 3.2 with IronPython 2.6:
import System.Drawing
import System.Drawing.Drawing2D
import System.Windows.Forms
from System import Array
from System.Drawing import Pen, Point
from System.Drawing.Drawing2D import GraphicsPath, CustomLineCap
from System.Windows.Forms import *
class MainForm(Form):
def __init__(self):
self.InitializeComponent()
def InitializeComponent(self):
self.SuspendLayout()
#
# MainForm
#
self.ClientSize = System.Drawing.Size(284, 264)
self.Name = "MainForm"
self.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
self.Text = "GDI Lines"
self.Paint += self.MainFormPaint
self.ResumeLayout(False)
def MainFormPaint(self, sender, e):
graphicContext = e.Graphics
bluePen = Pen(Color.Blue, 1)
points = Array.CreateInstance(Point, 9)
points[0] = Point(10, 10)
points[1] = Point(15, 10)
points[2] = Point(20, 15)
points[3] = Point(20, 20)
points[4] = Point(15, 25)
points[5] = Point(10, 25)
points[6] = Point(5, 20)
points[7] = Point(5, 15)
points[8] = Point(10, 10)
graphicsPath = GraphicsPath()
graphicsPath.AddLines(points)
graphicContext.SmoothingMode = SmoothingMode.AntiAlias
lineCap = CustomLineCap(nil, graphicsPath)
lineCap.BaseInset = 0
lineCap.WidthScale = 1
lineCap.StrokeJoin = LineJoin.Miter
bluePen.CustomStartCap = lineCap
bluePen.CustomEndCap = lineCap
graphicContext.DrawLine(bluePen, 50, 150, 200, 150)
graphicContext.DrawLine(bluePen, 150, 50, 150, 200)
lineCap.Dispose()
graphicsPath.Dispose()
bluePen.Dispose()
Based on the code above, I was expecting to see two perpendicualr blue lines drawn, with a small ellipse at the end of each line. Using the current scipting code above, the GDI+ runtime error red X is drawn. What am I missing or doing incorrectly? Also, is there a simpler or more concise way of instantiating an Array of System.Drawing.Point values?
Thank you in advance for your time and help...
In all fairness, I must say that I did not "Answer my own question" or resolve this issue on my own, but was able to receive outside help from both Matt Ward and Michael Foord. My sincerest thanks to both Matt and Michael for their time, help and patience, and I really appreciate them writing back with their corrections.
The main issue that kept the MainForm.py script from running was the omission on my part of importing the Color class from the System.Drawing namespace, and the SmoothingMode and LineJoin Enumerations from the System.Drawing.Drawing2D namespace. Although my script does not directly instantiate any of the additional enumerations or class, they still must be loaded and referenced from their respective assemblies by the .NET DLR to make them accessible and usable within my script. (Note: Again, thanks to Matt for pointing this out to me; if there are any errors in the explanation, they are mine and not Matt's.)
The original Array instantiation of the GDI+ Point instances was correct, but a more succinct approach is shown in the corrected script below. (Note: Again, my thanks to Michael for pointing out the Array instantiation alternative.)
The corrected and working MainForm.py script is as follows:
import System.Drawing
import System.Drawing.Drawing2D
import System.Windows.Forms
from System import Array
from System.Drawing import Pen, Point, Color
from System.Drawing.Drawing2D import GraphicsPath, CustomLineCap, SmoothingMode, LineJoin
from System.Windows.Forms import *
class MainForm(Form):
def __init__(self):
self.InitializeComponent()
def InitializeComponent(self):
self.SuspendLayout()
#
# MainForm
#
self.ClientSize = System.Drawing.Size(284, 264)
self.Name = "MainForm"
self.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
self.Text = "GDI+ CustomLineCaps"
self.Paint += self.MainFormPaint
self.ResumeLayout(False)
def MainFormPaint(self, sender, e):
graphics = e.Graphics
bluePen = Pen(Color.Blue, 1)
points = Array[Point] \
((Point(10, 10), Point(15, 10), Point(20, 15), \
Point(20, 20), Point(15, 25), Point(10, 25), \
Point(5, 20), Point(5, 15), Point(10, 10)))
graphicsPath = GraphicsPath()
graphicsPath.AddLines(points)
graphics.SmoothingMode = SmoothingMode.AntiAlias
lineCap = CustomLineCap(None, graphicsPath)
lineCap.BaseInset = 0
lineCap.WidthScale = 1
lineCap.StrokeJoin = LineJoin.Miter
bluePen.CustomStartCap = lineCap
bluePen.CustomEndCap = lineCap
graphics.DrawLine(bluePen, 50, 150, 200, 150)
graphics.DrawLine(bluePen, 150, 50, 150, 200)
lineCap.Dispose()
graphicsPath.Dispose()
bluePen.Dispose()
Related
Community,
i'm a complete noob in programming but i've learned quite a lot from stackoverflow (i hope...)
my question:
i'm working with ironpython and trying to create a GUI. Currently i'm struggling to get the KeyPress Event to work. I have TextBox and if someone presses the "Enter Key" it should do something. Like starting another function.
I'm using the IDE PyCharm to code.
I appreciate your help with this question. Thanks in advance :)
Here is what i've tried so far (just an example, not the real code):
import clr
# Set references
clr.AddReference("System.Drawing")
clr.AddReference("System.Windows.Forms")
# Import Winforms
from System.Drawing import *
from System.Windows.Forms import *
class MyClass(Form):
def __init__(self):
self.Text = "MyGui"
self.Size = Size(500, 500)
self.CenterToScreen()
self.create_textbox()
def create_textbox(self):
mytxtbox = TextBox()
mytxtbox.Name = "Textbox1"
mytxtbox.Location = Point(50, 50)
mytxtbox.Size = Size(100, 25)
mytxtbox.KeyPress += KeyPressEventHandler(self.press)
def press(self, sender, args):
key = args.KeyChar
if key == Keys.Enter:
print("You pressed Enter in the TextBox")
# Run this thang
form1 = MyClass()
Application.Run(form1)
You want to use KeyDown to get Enter key, instead of KeyPress:
mytxtbox.KeyDown += on_enter
def on_enter(self, e, args):
key = e.KeyChar
if key == Keys.Enter:
print("You pressed Enter in the TextBox")
I am trying to center a CheckBox widget vertically inside a panel. I cannot figure out why it's aligned to the top.
I had to create a Panel for the CheckBox because without that the background looks ugly (Windows 10 -- see second image)
Thanks for suggestions!
class wxUIE(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, size=(800, 400), title=title)
self.editorText = wx.TextCtrl (self, style=wx.TE_MULTILINE+wx.SUNKEN_BORDER+wx.TE_BESTWRAP)
self.myBar = wx.TextCtrl (self, -1)
# Checkbox
self.myCheckBoxPanel = wx.Panel(self, size=(-1, 25))
self.myCheckBox = wx.CheckBox(self.myCheckBoxPanel, label="Highlight", style=wx.ALIGN_CENTER_VERTICAL)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
self.sizer.Add(self.editorText, 1, wx.EXPAND)
self.sizer.Add(self.sizer2, 0, wx.EXPAND)
self.sizer2.Add(self.myBar, 10, wx.EXPAND)
self.sizer2.Add(self.myCheckBoxPanel, 0, wx.EXPAND)
self.SetSizeHints(800,400)
self.SetSizer(self.sizer)
self.SetAutoLayout(True)
self.Show()
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = wxUIE(None, "My Edit Control")
frame.Show()
app.MainLoop()
CheckBox aligned to top of panel
CheckBox is aligned vertically correctly but the background sucks.
I am creating a simple project using SceneKit and cannot get any shadows to appear in the Scene Editor or in the compiled application no matter what I try.
I have tried creating a simple box, placing it on a plane and adding a spot light. Code would be as follows:
// Create some properties
var scnView: SCNView!
var scnMasterScene: SCNScene!
var boxNode: SCNNode!
var cameraPerspNode: SCNNode!
var cameraOrthNode: SCNNode!
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
setupView()
createAndAddBoxScene()
setupCamera()
createAndAddSpotLight()
}
// Setup the view.
func setupView() {
scnView = self.sceneView as SCNView
// Set the scnView properties.
scnView.showsStatistics = true
scnView.autoenablesDefaultLighting = false
scnView.allowsCameraControl = true
// Create a Master scene.
scnMasterScene = SCNScene()
// Set the scene view's scene node to the master scene.
scnView.scene = scnMasterScene
}
// Setup the scene.
func createAndAddBoxScene() {
// Create a box of type SCNGeometry
let boxGeometry: SCNGeometry = SCNBox(width: 2000, height: 2000, length: 2000, chamferRadius: 100)
// Add a difuse colour to the box' first material.
boxGeometry.firstMaterial?.diffuse.contents = NSColor.redColor()
// Create a node of type SCNNode and attach the boxGeometry to it.
// Note: A node can only have 1 geometry object attached to it.
boxNode = SCNNode(geometry: boxGeometry)
// Add the new boxNode to the scene's root node.
scnMasterScene.rootNode.addChildNode(boxNode)
// Create a floor plane.
let floorGeometry: SCNGeometry = SCNPlane(width: 20000, height: 20000)
// Add a difuse colour to the floor's first material.
floorGeometry.firstMaterial?.diffuse.contents = NSColor.yellowColor()
// Create a floorPlaneNode and attach the floor plane to it.
let floorNode: SCNNode = SCNNode(geometry: floorGeometry)
// Tilt the floorPlaneNode in x.
let floorNodeTiltDegreesX: Double = -90
let floorNodeTiltRadiansX: Double = floorNodeTiltDegreesX * (π/180)
floorNode.rotation = SCNVector4Make(1, 0, 0, CGFloat(floorNodeTiltRadiansX))
// Add the floorPlaneNode to the master scene.
scnMasterScene.rootNode.addChildNode(floorNode)
}
// Create a camera, position it and add it to the scene.
func setupCamera() {
// Create a camera node which will be used to contain the camera.
cameraPerspNode = SCNNode()
// Create a new camera.
let cameraPersp: SCNCamera = SCNCamera()
// Set camera properties.
cameraPersp.name = "myPerspCamera"
cameraPersp.usesOrthographicProjection = false
cameraPersp.orthographicScale = 9
cameraPersp.xFov = 30
cameraPersp.zNear = 1
cameraPersp.zFar = 20000
// Assign the camera to the .camera property of the node.
cameraPerspNode.camera = cameraPersp
// Set the position and rotation of the camera node (NOT the camera).
cameraPerspNode.position = SCNVector3(x: 0, y: 4000, z: 6000)
let cameraPerspTiltDegrees: Double = -30
let cameraPerspTiltRadians: Double = cameraPerspTiltDegrees * (π/180)
cameraPerspNode.rotation = SCNVector4Make(1, 0, 0, CGFloat(cameraPerspTiltRadians))
// Add the new cameraNode to the scene's root.
scnMasterScene.rootNode.addChildNode(cameraPerspNode)
}
func createAndAddSpotLight() -> Void {
let spot = SCNLight()
spot.type = SCNLightTypeSpot
spot.castsShadow = true
spot.color = NSColor(hue: 1, saturation: 0, brightness: 0, alpha: 1)
spot.spotInnerAngle = 30
spot.spotOuterAngle = 60
let spotNode = SCNNode()
spotNode.light = spot
spotNode.position = SCNVector3(x: 0, y: 2000, z: 2000)
let lookAt = SCNLookAtConstraint(target: boxNode)
spotNode.constraints = [lookAt]
}
If I bring in a .dae filed add a spot light, or directional light, using the Scene Editor I can light the scene but there are no shadows when I set the Cast Shadows property in the Attributes Inspector.
Can anyone shine any light on my problem?
Thanks
The scene is large because the 3D model has been created at 1:1 and it depicts a large building. After much trial and error I finally found the solution - I changed the scale of the light to 10, 10, 10 and the shadows appeared.
the dimensions in your scene are huge (in SceneKit 1 unit = 1 meter). You'll want to change your light's zFar property (and probably zNear too) accordingly.
I developed an IronPython-wpf application with GUI using XAML. The user enter all the inputs in the GUI and I can save them. My problem is how I could pass those variables to my main python code after USER closes the GUI? To close the GUI, I have a button, which closes the GUI, If the USER click it.
What I did is as follows (I just copied some portion of the code):
import wpf
class MyApp(Window):
def __init__(self):
self.opencfgfile()
self.ictemp = self.FindName('pipent')
self.ictemp.Text = self.ictest
self.button = self.FindName('button')
self.button.Click += self.onClick
def onClick(self, sender, event):
self.Close()
def opencfgfile(self):
sstest = os.environ['test']
infile = open (sstest + '\config\test_Config.cfg' , 'r')
for line in infile:
if line != "\n":
line = line[:-1]
fields = line.split('=')
if fields[0] == 'Initial test ID ':
self.ictest = fields[1].lstrip()
def getsetname(self):
try:
return self.ictemp
except AttributeError:
return
if __name__ == "__main__":
c = MyApp()
Application().Run(c)
iset = c.getsetname()
In my class, if I put a break point, self.ictest has a value of 'test' and self.ictemp has a value of {System.Windows.Controls.TextBox:test}, however if I put the break point in my main program for iset, I will get this Value: 'The name iset does not exist in the current context. I really appreciate if you can help me on this issue.
Your problem is, that the code iset = c.getsetname() will only be reached, after the main window is closed. The reason for this is, that Application().Run() contains your "application main loop", which will run until the main window is closed. So in your case, you should implement all interaction logic in MyApp, not in the application entry point. Everything under if __name__ == "__main__" should only be used for initializing some modules or similar things.
If you want to encapsulate some logic, put it in own modules and call it from MyApp. For example if you want to react on some button click, just do this:
# Short
def __init__(self, ...):
# Init component
self.yourButton.Click += self.OnButtonClick
def OnButtonClick(self, sender, args):
MessageBox.Show("Clicked on some button!") # Import Message box before :)
Hope this helps.
EDIT
This is working pretty well:
import wpf
from System.Windows import Window, Application
from System.Windows.Controls import TextBox
class MyApp(Window):
box = None
def __init__(self):
self.Width = 200
self.Height = 200
self.box = TextBox()
self.box.Height = 24
self.box.Width = 150
self.Content = self.box
def getBoxContent(self):
return self.box.Text
if __name__ == "__main__":
c = MyApp()
Application().Run(c)
print (c.getBoxContent())
Info passing
If you want to pass info to and from the ui, just create some class which hold the information. This will be the best solution:
import wpf
from System.Windows import Window, Application
from System.Windows.Controls import TextBox
class Information:
InformationOne = 1
InformationTwo = "Hello World"
class MyApp(Window):
box = None
information = None
def __init__(self, info):
self.information = info
self.Width = 200
self.Height = 200
self.box = TextBox()
self.box.Height = 24
self.box.Width = 150
# Init information
self.box.Text = self.information.InformationTwo
self.Content = self.box
# Information property
#property
def Information(self):
self.information.InformationTwo = self.box.Text
return self.information
if __name__ == "__main__":
info = Information()
c = MyApp(info)
Application().Run(c)
# Get information
print (c.Information.InformationTwo)
everyone. There's probably a simple solution to this but I can't seem to find one. I'm playing around with the WebBrowser control in WPF that ships with Visual Studio 2010 and am trying to save an image that might appear on a webpage to disk programmatically.
Many thanks in advance!
Luck
Add System.Drawing as reference and perform the following oprations in the method that should capture the image:
Rect bounds = VisualTreeHelper.GetDescendantBounds(browser1);
System.Windows.Point p0 = browser1.PointToScreen(bounds.TopLeft);
System.Drawing.Point p1 = new System.Drawing.Point((int)p0.X, (int)p0.Y);
Bitmap image = new Bitmap((int)bounds.Width, (int)bounds.Height);
Graphics imgGraphics = Graphics.FromImage(image);
imgGraphics.CopyFromScreen(p1.X, p1.Y,
0, 0,
new System.Drawing.Size((int)bounds.Width,
(int)bounds.Height));
image.Save("C:\\a.bmp", ImageFormat.Bmp);
Here are the adaptions to solution of #luvieere:
WebBrowser browser1;
browser1 = this.Browser;
// I used the GetContentBounds()
Rect bounds = VisualTreeHelper.GetContentBounds(browser1);
// and the point to screen command for the top-left and the bottom-right corner
System.Windows.Point pTL = browser1.PointToScreen(bounds.TopLeft);
System.Windows.Point pBR = browser1.PointToScreen(bounds.BottomRight);
System.Drawing.Bitmap image = new
// The size is then calculated as difference of the two corners
System.Drawing.Bitmap(
System.Convert.ToInt32(pBR.X - pTL.X),
System.Convert.ToInt32(pBR.Y - pTL.Y));
System.Drawing.Graphics imgGraphics = System.Drawing.Graphics.FromImage(image);
imgGraphics.CopyFromScreen(pTL.X, pTL.Y, 0, 0, new System.Drawing.Size(image.Width, image.Height));
fileName = System.IO.Path.GetFileNameWithoutExtension(fileName) + ".bmp";
image.Save(fileName, System.Drawing.Imaging.ImageFormat.Bmp);
For those, who prefer VB-dialect
Dim browser1 As WebBrowser
browser1 = Me.Browser
Dim bounds As Rect = VisualTreeHelper.GetContentBounds(browser1)
Dim pTL As System.Windows.Point = browser1.PointToScreen(bounds.TopLeft)
Dim pBR As System.Windows.Point = browser1.PointToScreen(bounds.BottomRight)
Dim image As System.Drawing.Bitmap = New System.Drawing.Bitmap(CInt(pBR.X - pTL.X), CInt(pBR.Y - pTL.Y))
Dim imgGraphics As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(image)
imgGraphics.CopyFromScreen(pTL.X, pTL.Y, 0, 0, New System.Drawing.Size(image.Width, image.Height))
fileName = IO.Path.GetFileNameWithoutExtension(fileName) & ".bmp"
image.Save(fileName, System.Drawing.Imaging.ImageFormat.Bmp)